web-dev-qa-db-ja.com

EqualityComparer <T> .Default vs. T.Equals

タイプ_MyClass<T>_の2つのオブジェクトを比較する必要がある汎用_<T>_があるとします。通常、私は次のようなことをします...

_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>_に特別なジェネリック制約も使用する必要があることを意味しますか?)

32
takrl

同じである必要がありますが、タイプTの実装の詳細に依存するため、保証されません。
説明:
Tに制約がない場合、TObject.Equalsを実装している場合でも、o1.Equals(o2)はIEquatable<T>を呼び出します。
EqualityComparer<T>.Defaultは、ただし、TObject.Equalsを実装していない場合、IEquatable<T>のみを使用します。 doesそのインターフェースを実装する場合、IEquatable<T>.Equalsを使用します。
TObject.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を使用することをお勧めします。

40
Daniel Hilgarth

デフォルトでは、クラスでオーバーライドされるまで、Object.Equals(a,b)/a.Equals(b)は参照による比較を実行します。

EqualityComparer<T>.Defaultによって返される比較子は、Tによって異なります。たとえば、T : IEquatable<>の場合、適切なEqualityComparer<T>が作成されます。

3
abatishchev

Nullcoaelescense演算子を使用できますか?それが本当に重要な場合は短縮する

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

  }
2
Marino Šimić

これは、オブジェクトの作成時に比較子を指定しない場合に、BCL内のDictionary<>およびその他のジェネリックコレクションが行うこととまったく同じです。これの利点は、EqualityComparer<T>.DefaultIEquatable<T>型、null許容型、および列挙型に対して適切な比較子を返すことです。 Tがそれらのいずれでもない場合、古いコードが行っているように、単純なEquals比較が行われます。

2
Ryan Hauert

はい、タイプTが実装している場合はEqualityComparer<T>.Defaultの実装を使用し、そうでない場合はIEquatable<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))
    {
     ...
    }
}
2
AbdouMoumen