2011-05-02 134 views
27

假設我有一個通用的MyClass<T>,需要比較<T>類型的兩個對象。通常我會做這樣的事情......EqualityComparer <T>.Default與T.Equals

void DoSomething(T o1, T o2) 
{ 
    if(o1.Equals(o2)) 
    { 
    ... 
    } 
} 

現在假設我MyClass<T>具有支持通過自定義IEqualityComparer<T>,類似於Dictionary<T>構造。在這種情況下,我需要做的......

private IEqualityComparer<T> _comparer; 
public MyClass() {} 
public MyClass(IEqualityComparer<T> comparer) 
{ 
    _comparer = comparer; 
} 
void DoSomething(T o1, T o2) 
{ 
    if((_comparer != null && _comparer.Equals(o1, o2)) || (o1.Equals(o2))) 
    { 
    ... 
    } 
} 

要刪除這個漫長的if語句,它會是很好的,如果我能有_comparer默認如果使用常規構造一個「默認的比較」。我搜索了像typeof(T).GetDefaultComparer()之類的東西,但無法找到類似的東西。

我確實找到EqualityComparer<T>.Default,我可以使用它嗎?而隨後將這個片段中......

public MyClass() 
{ 
    _comparer = EqualityComparer<T>.Default; 
} 
void DoSomething(T o1, T o2) 
{ 
    if(_comparer.Equals(o1, o2)) 
    { 
    ... 
    } 
} 

...提供相同的結果作爲使用o1.Equals(o2)對所有可能的情況?

(作爲一個方面說明,這將意味着我還需要使用任何特殊的泛型約束的<T>?)

+1

注:可以按如下步驟做,如果'o1'爲null,'o1.Equals(O2)'將與無處不在的'NullReferenceException'失敗,而'EqualityComparer .Default.Equals(O1, o2)'會快樂地回報假。 – 2016-11-03 11:30:27

+0

@ M.Stramm這就是爲什麼我說「我會做類似......的事情」,而不是「這是我的產品代碼......」;-) – takrl 2017-07-24 08:15:26

回答

36

它應該是相同的,但它不能保證,因爲它依賴於實現細節類型爲T
說明:
沒有制約T,o1.Equals(O2)將調用Object.Equals,即使T實現IEquatable<T>
EqualityComparer<T>.Default但是,只會使用Object.Equals,如果T不執行IEquatable<T>。如果它確實實現該接口,它使用IEquatable<T>.Equals
只要T的執行Object.Equals只是調用IEquatable<T>.Equals的結果是一樣的。但在下面的例子中,結果是不一樣的:

public class MyObject : IEquatable<MyObject> 
{ 
    public int ID {get;set;} 
    public string Name {get;set;} 

    public override bool Equals(object o) 
    { 
     var other = o as MyObject; 
     return other == null ? false : other.ID == ID; 
    }  

    public bool Equals(MyObject o) 
    { 
     return o.Name == Name; 
    } 
} 

現在,它沒有任何意義,實現這樣一個類。但是,如果MyObject的實施者僅僅忘記覆蓋Object.Equals,您將會遇到同樣的問題。

結論:
使用EqualityComparer<T>.Default是一個很好的方式,因爲您不需要支持錯誤的對象!

+15

任何類型的IEquatable.Equals和object.Equals做不同的事情是已經註定了:) – 2011-05-02 13:28:12

+0

謝謝。根據我的用例,我甚至可以指定一個約束,比如'public MyClass ,其中T:IEquatable '。好東西,你們都非常有幫助。 – takrl 2011-05-03 08:07:30

+2

返回語句看起來更像'return other!= null && other.ID == ID;' – nawfal 2013-12-15 10:04:13

3

默認情況下,直到在類中被覆蓋,Object.Equals(a,b)/a.Equals(b)通過引用執行比較。

EqualityComparer<T>.Default將返回什麼比較器取決於T。例如,如果T : IEquatable<>那麼將創建適當的EqualityComparer<T>

+0

IEquatable 而不是IComparable – 2011-05-02 13:28:16

+0

@保羅:當然,是通過記憶來寫的。已編輯。 – abatishchev 2011-05-02 13:29:21

2

你可以使用null coaelescense操作符?縮短,如果,如果它真的很重要

if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2)) 
    { 

    } 
+4

這是如何回答這個問題的? – 2016-11-03 11:31:40

2

這正是Dictionary<>和其他在BCL泛型集合做,如果構造對象時不指定比較器。這樣做的好處是,EqualityComparer<T>.Default將返回IEquatable<T>類型,可空類型和枚舉的右比較器。如果T不是這些,那麼它就會像舊代碼那樣進行簡單的Equals比較。

2

是的,我認爲這將是明智的使用EqualityComparer<T>.Default,因爲它使用的IEquatable<T>實施,如果該類型T實現它,或Object.Equals的覆蓋,否則。

private IEqualityComparer<T> _comparer; 
public IEqualityComparer<T> Comparer 
{ 
    get { return _comparer?? EqualityComparer<T>.Default;} 
    set { _comparer=value;} 
} 
public MyClass(IEqualityComparer<T> comparer) 
{ 
    _comparer = comparer; 
} 
void DoSomething(T o1, T o2) 
{ 
    if(Comparer.Equals(o1, o2)) 
    { 
    ... 
    } 
} 
相關問題