FxCopには 、
Equalsのオーバーライドに関する
GetHashCodeのオーバーライド、
Equalsをオーバーライドする
GetHashCodeのオーバーライド
に関するルールがあります。 したがって、落とし穴はこのルールに関連付けられています。 ルールの説明にはこれについて書かれていますが、私の意見では完全に明確ではありません。
これは、.NETの
HashTableと
Dictionaryの動作
原理によるものであり、
Equalsをオーバーライドするときに比較を正しく実行するには、比較に関係するデータに応じて
GetHashCodeを再定義する必要があります。 それ以外の場合、
Equalsは単に呼び出されませんが、多くの開発者は常に呼び出されることを期待しています。
Equalsが呼び出されるのは、
GetHashCodeが同じ値を返す場合のみです。これは、前述のように、ハッシュテーブルの衝突を解決するために、辞書とハッシュテーブルの動作原理に関連しています。
一方、
GetHashCodeメソッドがオブジェクトに格納されたデータに依存しない値を返し、同一のオブジェクトに対して異なるハッシュコードを返す
System.Objectの動作は
、このような誤った結論によって促される可能性が非常に高いです。
そして、そのような振る舞いは些細なことのように思えますが、多くの場合、一度にこの落とし穴につまずきます。
この一般的なエラーは、例で検討できます。
実際、ある種の
CustomTypeがあり、実際には2つの
Nameフィールドと
Ageフィールドがあります
。public class NamesComparer : IEqualityComparer<CustomType>
{
#region IEqualityComparer<CustomType> Members
public bool Equals(CustomType x, CustomType y)
{
return string .Equals(x.Name, y.Name);
}
public int GetHashCode(CustomType obj)
{
// ,
return obj.GetHashCode();
}
#endregion
}
[DebuggerDisplay( "Name: {Name}, age: {Age}" )]
public class CustomType : IEqualityComparer<CustomType>
{
public CustomType()
{
}
public CustomType( string name, int age)
{
Name = name;
Age = age;
}
public int Age { get ; set ; }
public string Name { get ; set ; }
public override bool Equals(CustomType x, CustomType y)
{
return string .Equals(x.Name, y.Name);
}
public override int GetHashCode(CustomType obj)
{
// ,
return obj.GetHashCode();
}
}
次の2つのリストがあります。
CustomType[] customTypeShortList = new [] {
new CustomType( "reno" , 1),
new CustomType( "toyota" , 3) };
CustomType[] customTypeLongList = new [] {
new CustomType( "audi" , 5),
new CustomType( "opel" , 7),
new CustomType( "reno" , 10),
new CustomType( "subaru" , 5),
new CustomType( "toyota" , 4),
new CustomType( "nissan" , 3)};
linq演算子
Intersectで交差点を見つけます。 複合非プリミティブ型を
使用しているため、この型に
IEqualityComparerを指定する必要があります。
IEnumerable <CustomType> intersect = customTypeLongList
.Intersect(customTypeShortList, new NamesComparer());
その結果、
インターセクションリストでreno-10とtoyota-4の2つの値を取得する予定です。ただし、
CustomTypeの GetHashCodeが正しく実装されていないため、取得できません。 各オブジェクトは全体として異なるハッシュコードを持っているため、
Equalsメソッドは呼び出されません。
この場合の正しい実装はオプションです:
public int GetHashCode(CustomType obj)
{
return obj.Name.GetHashCode();
}
このオプションを試すことはできますが、確かにそうではありませんが、
Equalsが常に呼び出されるため、交差は正しく機能します。
public int GetHashCode(CustomType obj)
{
// , -
return string .Empty.GetHashCode();
}
したがって、
Equalsを実装する場合、
Equalsの比較に参加するデータを考慮して、
GetHashCodeを実装する必要があります
。 そうでない場合、
Equalsを呼び出すことさえできません。
多くの人々がこの落とし穴に遭遇するので、私はそれについて書くことにしました。
コメントplsc_Roverから:
GetHashCodeオブジェクトをオーバーライドするのではなく、IEqualityComparerのGetHashCodeの実装を混同しないようにすることも重要です。 2番目の場合、辞書は正しく機能しません。