31

GetHashCode を実装する方法 (実装が必要な場合) の 1 つは、Jon Skeet によってここで概説されています。彼のコードを繰り返す:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

このコードを手作業でローリングすると、エラーが発生しやすく、バグがわかりにくい/見つけにくい場合があります (スワップ+したり*、間違えたりしましたか?)。さまざまなタイプの組み合わせルールを覚えるのが難しい場合があります。さまざまな分野やクラスについて、同じことを何度も何度も書いたり見直したりする努力。また、最も重要な詳細の 1 つ (すべてのフィールドを含めることを覚えていましたか?) が繰り返しノイズでわかりにくくなる可能性もあります。

.net ライブラリを使用してフィールド ハッシュコードを組み合わせる簡潔な方法はありますか? . もちろん、自分で書くこともできますが、慣用的な/組み込みのものがあれば、それを好むでしょう。

例として、Java (JDK7 を使用) では、次を使用して上記を実現できます。

   @Override
   public int hashCode()  
   {  
      return Objects.hash(field1, field2, field3);  
   }  

これは、バグを排除し、重要な詳細に集中するのに本当に役立ちます.

動機: オーバーライドさGetHashCode()れた . このようなバグを回避するには、ハッシュコードを結合するためのライブラリ関数が役立ちます。

4

5 に答える 5

20

一部の人々は使用します:

Tuple.Create(lastName, firstName, gender).GetHashCode()

これは、MSDNObject.GetHashCode()の に記載されており、次の警告が表示されます。

ただし、Tuple オブジェクトのインスタンス化のパフォーマンス オーバーヘッドは、ハッシュ テーブルに多数のオブジェクトを格納するアプリケーションの全体的なパフォーマンスに大きな影響を与える可能性があることに注意してください。

構成要素のハッシュを集約するロジックは、 によって提供されSystem.Tupleます。

更新: コメントで @Ryan の観察に注目する価値はありますが、これはサイズが 8 を超える任意のタプルの最後の 8 つの要素のみを使用しているように見えます。

于 2013-08-05T18:50:06.997 に答える
20

編集: System.HashCodeリリースされました。ハッシュコードを作成する推奨される方法は次のとおりです。

public override int GetHashCode()
{
    return HashCode.Combine(fieldA, fieldB, fieldC);
}

System.HashCode.Combine()各フィールドを内部的に呼び出し.GetHashCode()、正しいことを自動的に行います。

非常に多くのフィールド (8 つ以上) の場合、インスタンスを作成してからメソッドHashCodeを使用できます。.Add()

public override int GetHashCode()
{
    HashCode hash = new HashCode();
    hash.Add(fieldA);
    hash.Add(fieldB);
    hash.Add(fieldC);
    hash.Add(fieldD);
    hash.Add(fieldE);
    hash.Add(fieldF);
    hash.Add(fieldG);
    hash.Add(fieldH);
    hash.Add(fieldI);
    return hash.ToHashCode();
}

Visual Studio 2019 には、生成するクイック アクション ヘルパーが追加されEquals()ましGetHashCode()た。宣言内のクラス名を右クリックし、[クイック アクションとリファクタリング] > [Equals と GetHashCode を生成] をクリックします。等価に使用するメンバーを選択し、「Implement IEquatable」も選択して、[OK] をクリックします。

最後にもう 1 つ: オブジェクトの構造ハッシュ コードを取得する必要がある場合、たとえば、参照ではなく内容(別名構造) に基づいて変化する配列のハッシュ コードを含めたい場合は、キャストする必要があります。次のように、フィールドを手動で取得し、そのハッシュ コードを手動で取得します。IStructuralEquatable

public override int GetHashCode()
{
    return HashCode.Combine(
        fieldA,
        ((IStructuralEquatable)stringArrayFieldB).GetHashCode(EqualityComparer<string>.Default));
}

これは、IStructuralEquatableほとんどの場合、インターフェイスが明示的に実装されているためです。そのため、デフォルトメソッドの代わりに へのキャストIStructuralEquatableを呼び出す必要があります。IStructuralEquatable.GetHashCode()object.GetHashCode()

最後に、現在の実装で.GetHashCodeは、 のintは単に整数値そのものであるためHashCode.Combine()、フィールド自体の代わりに にハッシュコード値を渡しても、結果に違いはありません。

古い答え:

完全を期すために、.NET タプル リファレンス ソースの 52 行目にあるハッシュ アルゴリズムを次に示します。興味深いことに、このハッシュ アルゴリズムは からコピーされたものSystem.Web.Util.HashCodeCombinerです。

コードは次のとおりです。

public override int GetHashCode() {
    // hashing method taken from .NET Tuple reference
    // expand this out to however many items you need to hash
    return CombineHashCodes(this.item1.GetHashCode(), this.item2.GetHashCode(), this.item3.GetHashCode());
}

internal static int CombineHashCodes(int h1, int h2) {
    // this is where the magic happens
    return (((h1 << 5) + h1) ^ h2);
}

internal static int CombineHashCodes(int h1, int h2, int h3) {
    return CombineHashCodes(CombineHashCodes(h1, h2), h3);
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4) {
    return CombineHashCodes(CombineHashCodes(h1, h2), CombineHashCodes(h3, h4));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), h5);
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8));
}

もちろん、実際の Tuple GetHashCode()(実際には) には、保持しているアイテムの数に基づいて、これらのどれを呼び出すかを決定するInt32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer)大きなブロックがあります。独自のコードでは、おそらくそれを必要としません。switch

于 2016-05-26T00:14:54.913 に答える
10

まったく同じではありませんが、 Noda TimeHashCodeHelperにクラスがあります (これには、等価性とハッシュ コード操作をオーバーライドする多くの型があります)。

次のように使用されます (から取得ZonedDateTime):

public override int GetHashCode()
{
    int hash = HashCodeHelper.Initialize();
    hash = HashCodeHelper.Hash(hash, LocalInstant);
    hash = HashCodeHelper.Hash(hash, Offset);
    hash = HashCodeHelper.Hash(hash, Zone);
    return hash;
}

これは、値型のボックス化を回避するジェネリック メソッドであることに注意してください。null 値に自動的に対処します (値に 0 を使用)。Noda Time はプロジェクト設定としてチェック演算を使用するため、MakeHashメソッドにはブロックがあることに注意してください。一方、ハッシュ コード計算はオーバーフローを許可する必要があります。unchecked

于 2013-08-05T20:05:49.170 に答える