独自クラスのGetHashCode()をお手軽に実装する方法

この記事は公開から4年以上経過しています。

.NET Framework開発で独自クラスのGetHashCode()をオーバーライドする場合、各プロパティのハッシュ値に対して素数とのビット演算など複雑な演算を行う必要がありますが、衝突の少ないハッシュアルゴリズムを考えるのは容易ではありません。

このような場合には、匿名クラスを利用すると簡単に実現できます。

.NET Core 2.1以降ではSystem.HashCodeクラスも利用できます。

// C#

class MyData
{
    public string Text { get; set; }
    public int IntNumber { get; set; }
    public bool Boolean { get; set; }

    public override bool Equals(object obj)
    {
        // ハッシュ値で比較
        return GetHashCode() == obj.GetHashCode();
    }

    public override int GetHashCode()
    {
        // 匿名クラスのハッシュ値を返す
        return new { Text, IntNumber, Boolean }.GetHashCode();
    }
}

要は構造体のハッシュ値を扱うイメージです。
実際に検証コードを書いて、どのようなハッシュ値になるのか確認してみます。

// C#

// テストコード
void Main()
{
    // ベース
    Console.WriteLine($"1. {new MyData { Text = "test", IntNumber = 1, Boolean = true }.GetHashCode()}");
    // ベースからTextのみ変更
    Console.WriteLine($"2. {new MyData { Text = "changed", IntNumber = 1, Boolean = true }.GetHashCode()}");
    // ベースからIntNumberのみ変更
    Console.WriteLine($"3. {new MyData { Text = "test", IntNumber = 2, Boolean = true }.GetHashCode()}");
    // ベースからBooleanのみ変更
    Console.WriteLine($"4. {new MyData { Text = "test", IntNumber = 1, Boolean = false }.GetHashCode()}");
    // ベースからText/IntNumber/Boolean全てを変更
    Console.WriteLine($"5. {new MyData { Text = "changed", IntNumber = 2, Boolean = false }.GetHashCode()}");
    // ベースと同じ値、但し別インスタンス
    Console.WriteLine($"6. {new MyData { Text = "test", IntNumber = 1, Boolean = true }.GetHashCode()}");
}
1. -1090232970
2. -1186768460
3. 1683600031
4. -1090232971
5. 1587064540
6. -1090232970

上記のとおり、同一インスタンスのオブジェクトではなく同一プロパティ値を持つオブジェクトのハッシュ値が同じになることが分かります。
VB.NETでも同様ですが、匿名クラスのプロパティがキープロパティになることに注意が必要です。

' VB.NET

Class MyData
    Public Property Text As String
    Public Property IntNumber As Integer
    Public Property Bool As Boolean

    Public Overrides Function Equals(obj As Object) As Boolean
        ' ハッシュ値で比較
        Return GetHashCode() = obj.GetHashCode()
    End Function

    Public Overrides Function GetHashCode() As Integer
        ' 匿名クラスのハッシュ値を返す
        Return New With {Key .Text = Text, Key .IntNumber = IntNumber, Key .Bool = Bool}.GetHashCode()
    End Function
End Class

このような対応を行いたいと感じた場合、クラスではなく構造体を使うべきというソフトウェア設計上の問題である場合もありますが、構造体が利用できないケースなどでは役に立つかも知れません。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする