Visual Studioが生成されたハッシュコード計算に「-1937169414」を追加するのはなぜですか?


9

Visual Studio独自のリファクタリングメニューを使用して、次のようにGetHashCode実装をクラスに追加する場合:

GetHashCodeメニューを生成する

クラスで唯一のintプロパティを選択します。

会員選択画面

.NET Frameworkでこのコードを生成します。

public override int GetHashCode()
{
    return -1937169414 + Value.GetHashCode();
}

HashCode.Combine(Value)代わりに.NET Core で生成されますが、同じ値が含まれるかどうかはわかりません)

この値の特別な点は何ですか?Visual StudioがValue.GetHashCode()直接使用しないのはなぜですか?私が理解しているように、それは実際にはハッシュ配布に影響を与えません。これは単なる加算なので、連続する値は引き続き一緒に蓄積されます。

編集:私はこれをValueプロパティを持つ異なるクラスでのみ試しましたが、明らかにプロパティ名は生成される数に影響します。たとえば、プロパティの名前をに変更するHalueと、番号は387336856になります。これを指摘してくれたGökhanKurtに感謝します。


備考セクションのdocs.microsoft.com/en-us/dotnet/api/…を参照してください。「同じ文字列のハッシュコードは、.NETの実装間、.NETバージョン間、および.NETの単一バージョンの.NETプラットフォーム(32ビットや64ビットなど)間で異なる場合があります。場合によっては、アプリケーションドメイン別」
リンク

@Linkそれはどのように関連していますか?それも文字列ではなく、プロパティはintです。
セダットカパノグル

[HashCode] .Combine?
Ry-

申し訳ありませんが間違ったリンク:docs.microsoft.com/en-us/dotnet/api/… この動作は、Object.GetHashcode @SedatKapanogluにも適用されます
リンク

2
-1937169414-1521134295およびの整数乗算です-783812246。ここでより重要な数は-1521134295、すべてのハッシュコード計算で表示されます。-783812246シード番号です。シード番号は、方程式のメンバー数に基づいて選択されます。匿名クラスでは、シード番号はフィールド名に基づいて計算されます。したがって、整数の数と同じ数のシード番号があります。シード番号はランダムであると想定できます。の重要性については-1521134295、衝突を減らし、内部の開発者だけがどのように正確に答えることができると思います。
グーカンクルト

回答:


2

-1521134295Microsoftのリポジトリを探すと、かなりの回数表示されることがわかります。

ほとんどの検索結果はGetHashCode関数内にありますが、すべて次の形式になっています

int hashCode = SOME_CONSTANT;
hashCode = hashCode * -1521134295 + field1.GetHashCode();
hashCode = hashCode * -1521134295 + field2.GetHashCode();
// ...
return hashCode;

1つ目hashCode * -1521134295 = SOME_CONSTANT * -1521134295は、ジェネレータによる生成時またはCSCによるコンパイル時に事前に乗算されます。それ-1937169414があなたのコードの理由です

結果をさらに掘り下げると、関数CreateGetHashCodeMethodStatementsにあるコード生成部分が明らかになります

const int hashFactor = -1521134295;

var initHash = 0;
var baseHashCode = GetBaseGetHashCodeMethod(containingType);
if (baseHashCode != null)
{
    initHash = initHash * hashFactor + Hash.GetFNVHashCode(baseHashCode.Name);
}

foreach (var symbol in members)
{
    initHash = initHash * hashFactor + Hash.GetFNVHashCode(symbol.Name);
}

ご覧のとおり、ハッシュはシンボル名によって異なります。その関数では、定数はとも呼ばれますpermuteValue。これは、おそらく乗算後にビットが何らかの方法で並べ替えられるためです

// -1521134295
var permuteValue = CreateLiteralExpression(factory, hashFactor);

値をバイナリで表示する場合、いくつかのパターンがあります:101001 010101010101010 101001 01001または10100 1010101010101010 10100 10100 1。しかし、これに任意の値を掛けると、重なり合うキャリーがたくさんあるので、それがどのように機能するのかわかりませんでした。出力には設定ビット数が異なる場合があるため、実際には順列ではありません

定数を呼び出すRoslynのAnonymousTypeGetHashCodeMethodSymbolに別のジェネレーターを見つけることができますHASH_FACTOR

//  Method body:
//
//  HASH_FACTOR = 0xa5555529;
//  INIT_HASH = (...((0 * HASH_FACTOR) + GetFNVHashCode(backingFld_1.Name)) * HASH_FACTOR
//                                     + GetFNVHashCode(backingFld_2.Name)) * HASH_FACTOR
//                                     + ...
//                                     + GetFNVHashCode(backingFld_N.Name)

その値を選択する本当の理由はまだ不明です


これはすばらしい研究です、ありがとう。ハッシュコードの生成がRoslynで行われていることを知りませんでした。VisualStudio自体だと思いました。
セダットカパノグル

3

以下のようGökhanKurtはコメントで説明し、番号の変更が関与プロパティ名に基づきます。プロパティの名前をに変更すると、Halue代わりに番号は387336856になります。さまざまなクラスで試してみましたが、プロパティの名前を変更することは考えていませんでした。

Gökhanのコメントは私にその目的を理解させました。これは、確定的だがランダムに分散されたオフセットに基づいてハッシュ値をオフセットします。このように、異なるクラスのハッシュ値を組み合わせるだけで、単純な追加でも、ハッシュの衝突に対する耐性はわずかです。

たとえば、類似したGetHashCode実装を持つ2つのクラスがある場合:

public class A
{
    public int Value { get; set;}
    public int GetHashCode() => Value;
}

public class B
{
    public int Value { get; set;}
    public override int GetHashCode() => Value;
}

これらの2つへの参照を含む別のクラスがある場合:

public class C
{
    public A ValueA { get; set; }
    public B ValueB { get; set; }
    public override int GetHashCode()
    {
        return ValueA.GetHashCode() + ValueB.GetHashCode();
    }
}

結果のハッシュコードは、値が互いに近い場合、ValueAとValueBの異なる値に対して同じ領域の周りに蓄積するため、このような不適切な組み合わせはハッシュの衝突を起こしやすくなります。乗算またはビット単位の演算を使用してそれらを組み合わせるかどうかは、実際には問題ではありません。それらは、均等な距離のオフセットがないと衝突する傾向があります。プログラミングで使用される多くの整数値は0の周りに累積されるため、そのようなオフセットを使用することは理にかなっています

どうやら、ランダムなオフセットを適切なビットパターンで設定することをお勧めします。

GetHashCode()の決定論に依存するコードを壊さないために、完全にランダムなオフセットを使用しない理由はまだわかりませんが、Visual Studioチームからこれについてのコメントを受け取るのは素晴らしいことです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.