38

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)))
  {
    ...
  }
}

_comparerこの長いifステートメントを削除するには、通常のコンストラクターを使用する場合に、デフォルトを「デフォルトの比較子」にすることができればよいでしょう。のようなものを検索しましたが、そのようなもの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>ますか?)

4

5 に答える 5

49

同じになるはずですが、 type の実装詳細に依存するため、保証されませんT
説明:
に対する制約がなければT、 o1.Equals(o2) は を実装Object.Equalsしていてもを呼び出します。ただし、実装しない場合のみ使用します。そのインターフェイスを実装する場合、 . の呼び出しだけの実装である 限り、結果は同じです。ただし、次の例では、結果は同じではありません。 TIEquatable<T>
EqualityComparer<T>.DefaultObject.EqualsTIEquatable<T>IEquatable<T>.Equals
TObject.EqualsIEquatable<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

于 2011-05-02T13:24:37.293 に答える
4

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

どの比較子が返されるかは、EqualityComparer<T>.Defaultに依存しTます。たとえばT : IEquatable<>、適切なEqualityComparer<T>が作成されます。

于 2011-05-02T13:27:06.587 に答える
3

はい、型がそれを実装している場合EqualityComparer<T>.Defaultは の実装を使用するか、そうでない場合は のオーバーライドを使用するため、を使用するのが賢明だと思います。次のように実行できます。IEquatable<T>TObject.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))
    {
     ...
    }
}
于 2011-05-02T13:29:46.483 に答える
2

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

于 2011-05-02T13:29:39.090 に答える
2

null coaelescense 演算子を使用できますか?? それが本当に重要ならifを短くする

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

  }
于 2011-05-02T13:27:29.593 に答える