等しいとGetHashCodeを比較する

FxCopにはEqualsのオーバーライドに関するGetHashCodeのオーバーライド、 EqualsをオーバーライドするGetHashCodeのオーバーライドに関するルールがあります。 したがって、落とし穴はこのルールに関連付けられています。 ルールの説明にはこれについて書かれていますが、私の意見では完全に明確ではありません。



これは、.NETのHashTableDictionaryの動作原理によるものであり、 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番目の場合、辞書は正しく機能しません。



All Articles