タイプ_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>
_に特別なジェネリック制約も使用する必要があることを意味しますか?)
同じである必要がありますが、タイプT
の実装の詳細に依存するため、保証されません。
説明:T
に制約がない場合、T
がObject.Equals
を実装している場合でも、o1.Equals(o2)はIEquatable<T>
を呼び出します。EqualityComparer<T>.Default
は、ただし、T
がObject.Equals
を実装していない場合、IEquatable<T>
のみを使用します。 doesそのインターフェースを実装する場合、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
を使用することをお勧めします。
デフォルトでは、クラスでオーバーライドされるまで、Object.Equals(a,b)
/a.Equals(b)
は参照による比較を実行します。
EqualityComparer<T>.Default
によって返される比較子は、T
によって異なります。たとえば、T : IEquatable<>
の場合、適切なEqualityComparer<T>
が作成されます。
Nullcoaelescense演算子を使用できますか?それが本当に重要な場合は短縮する
if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2))
{
}
これは、オブジェクトの作成時に比較子を指定しない場合に、BCL内のDictionary<>
およびその他のジェネリックコレクションが行うこととまったく同じです。これの利点は、EqualityComparer<T>.Default
がIEquatable<T>
型、null許容型、および列挙型に対して適切な比較子を返すことです。 Tがそれらのいずれでもない場合、古いコードが行っているように、単純なEquals比較が行われます。
はい、タイプ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))
{
...
}
}