2009-11-02 27 views
1

我想能夠比較兩個派生自C#中相同抽象類的類。以下代碼說明了我的問題。如何根據基類來比較兩個對象?

我現在可以通過使BaseClass非抽象,然後在ToBassClass()中返回new BaseClass對象來修復代碼。但是,是不是有一個更優雅和高效的解決方案?

abstract class BaseClass 
{ 
    BaseClass(int x) 
    { 
     X = x; 
    } 

    int X { get; private set; } 

    // It is probably not necessary to override Equals for such a simple class, 
    // but I've done it to illustrate my point. 
    override Equals(object other) 
    { 
     if (!other is BaseClass) 
     { 
      return false; 
     } 

     BaseClass otherBaseClass = (BaseClass)other; 

     return (otherBaseClass.X == this.X); 
    } 

    BaseClass ToBaseClass() 
    { 
     // The explicit is only included for clarity. 
     return (BaseClass)this; 
    } 
} 

class ClassA : BaseClass 
{ 
    ClassA(int x, int y) 
     : base (x) 
    { 
     Y = y; 
    } 

    int Y { get; private set; } 
} 

class ClassB : BaseClass 
{ 
    ClassB(int x, int z) 
     : base (x) 
    { 
     Z = z; 
    } 

    int Z { get; private set; } 
} 

var a = new A(1, 2); 
var b = new B(1, 3); 

// This fails because despite the call to ToBaseClass(), a and b are treated 
// as ClassA and ClassB classes so the overridden Equals() is never called. 
Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass()); 
+3

Assert.True()? – SoftMemes 2009-11-02 14:48:53

+1

另外,int Z {get;私人Z; }應該是int Z {get;私人設置; }? – GraemeF 2009-11-02 14:51:55

回答

2

這取決於其中正是你想測試的平等。很明顯,ClassAClassB實例永遠不會在該單詞的真正意義上「相等」,因此重寫Equals表現爲這樣可能實際上會在代碼中導致一些奇怪的錯誤。

但是,如果您想根據特定條件比較它們,那麼您可以實現一個特定的IEqualityComparer(或幾個比較器),以滿足您的需求。

因此,在這種情況下,你會:

/// <Summary> 
/// Compares two classes based only on the value of their X property. 
/// </Summary> 
public class ComparerByX : IEqualityComparer<BaseClass> 
{ 
    #region IEqualityComparer<BaseClass> Members 

    public bool Equals(BaseClass a, BaseClass b) 
    { 
     return (a.X == b.X); 
    } 

    public int GetHashCode(BaseClass obj) 
    { 
     return obj.X.GetHashCode(); 
    } 

    #endregion 

} 

[編輯]關於評論:

請注意,這並不與重寫Equals方法什麼。

但是,你將能夠檢查平等這樣的:

IEqualityComparer<BaseClass> comparer = new ComparerByX(); 
Assert.True(comparer.Equals(a, b)); 

這似乎並不像起初一個偉大的事情,但它給你幾個優點:

一)你可以根據需要安裝儘可能多的IEqualityComparer<T>實現。根據不同的情況,可能會出現你重寫並不是那麼好的事情。那麼你冒險打破所有的代碼取決於這一點。

b)實際上有很多類使用IEqualityComparer<T>來比較項目。

例如,您可能希望使用BaseClass作爲字典中的鍵。在這種情況下,你可以使用它接受一個IEqualityComparer<T>Dictionary<Key,Value>構造函數重載:

Dictionary<BaseClass, SomeOtherClass> dictionary 
    = new Dictionary<BaseClass, SomeOtherClass>(new ComparerByX()); 

這樣,字典將使用自定義ComparerByX鍵查找中。另外,例如,如果您使用的是LINQ,則可以檢查Distinct()方法示例。它也支持一個超載,它返回不同的值,但使用指定的自定義IEqualityComparer進行比較。

+0

這會使a.ToBaseClass()== b.ToBaseClass(),並保持!= b? – 2009-11-02 15:13:14

2

嗯,弗裏德指出,這是一個有點奇怪在這裏使用Assert.True - 你的意思Assert.AreEqual?如果是這樣的話,我預料到這會起作用(即使沒有調用ToBaseClass),儘管它將取決於測試框架。

儘管如此,平等對於繼承來說很棘手。就個人而言,我會創建一個合適的IEqualityComparer<BaseClass>,明確地說「我要測試對象的這個特定方面」 - 這意味着繼承基本上不涉及。

2

首先,你的代碼不能編譯。其次,當您的代碼被修復以便編譯時(特別是,Assert.True更改爲Assert.AreEqual),我會看到您期望的結果。這是一件好事,因爲這是正確的行爲。但是你不能依賴繼承者而不是覆蓋Object.Equals,所以如果你想比較基類,那麼你應該實現IEqualityComparer<BaseClass>

這是你的代碼的版本,你可能是爲了它,以免它編譯:

abstract class BaseClass { 
    public BaseClass(int x) { X = x; } 

    public int X { get; private set; } 

    public override bool Equals(object other) { 
     if (!(other is BaseClass)) { 
      return false; 
     } 

     BaseClass otherBaseClass = (BaseClass)other; 
     return (otherBaseClass.X == this.X); 
    } 

    public BaseClass ToBaseClass() { 
     return (BaseClass)this; 
    } 
} 

class ClassA : BaseClass { 
    public ClassA(int x, int y) : base (x) { 
     Y = y; 
    } 

    public int Y { get; private set; } 
} 

class ClassB : BaseClass { 
    public ClassB(int x, int z) : base (x) { 
     Z = z; 
    } 

    public int Z { get; private set; } 
} 

class Program { 
    static void Main(string[] args) { 
     var a = new ClassA(1, 2); 
     var b = new ClassB(1, 3); 
     Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass()); 
    } 
} 
1

我認真推薦使用KellermanSoftware.CompareNetObjects(雖然我不是作家 - 這是非常靈活的,有效的,到目前爲止,沒有錯誤!)。

我做了同樣的事情來比較基於基類的2個對象。

1)創建一個 「BaseClassComparer」 帶您要使用的比較類型:

using System; 
using KellermanSoftware.CompareNetObjects; 

namespace Compare 
{ 

    /// <summary> 
    /// This allows us to compare objects based on a particular class (ie so we can compare on base classes) 
    /// </summary> 
    public class BaseClassComparer : KellermanSoftware.CompareNetObjects.TypeComparers.ClassComparer 
    { 


     private readonly Type _compareType; 
     internal BaseClassComparer(Type compareType, RootComparer rootComparer) : base(rootComparer) 
     { 

      _compareType = compareType; 
     } 

     public override void CompareType(CompareParms parms) 
     { 
      parms.Object1Type = _compareType; 
      parms.Object2Type = _compareType; 

      base.CompareType(parms); 
     } 

     public override bool IsTypeMatch(Type type1, Type type2) 
     { 
      if (((_compareType.IsAssignableFrom(type1)) && (_compareType.IsAssignableFrom(type2)))) { 
       return true; 
      } else { 
       return false; 
      } 
     } 
    } 
} 

然後,這個類添加到凱勒曼比較器:

_compare = New CompareLogic 
_compare.Config.CustomComparers.Add(New BaseClassComparer(compareType, RootComparerFactory.GetRootComparer())) 

而且比較遠。只有基本類型(compareType)之間的區別將被檢查/報告...