57

Eric Lippert によるGetHashCode のガイドラインとルールから引用するには:

ルール: GetHashCode のコンシューマは、長期にわたって、またはアプリケーション ドメイン間で安定していることに依存できません。

Name、Address などの多数のフィールドを持つ Customer オブジェクトがあるとします。2 つの異なるプロセスでまったく同じデータを持つ 2 つのオブジェクトを作成した場合、それらは同じハッシュ コードを返す必要はありません。火曜日に 1 つのプロセスでそのようなオブジェクトを作成し、それをシャットダウンして、水曜日にプログラムを再度実行すると、ハッシュ コードが異なる可能性があります。

これは過去に人々を噛んだ。System.String.GetHashCode のドキュメントでは、CLR のバージョンが異なると 2 つの同一の文字列が異なるハッシュ コードを持つ可能性があり、実際にそうであることが具体的に記載されています。文字列ハッシュをデータベースに保存しないでください。それらが永遠に同じであることを期待してはいけません。

では、データベースに格納できる文字列の HashCode を作成する正しい方法は何ですか?

(私が書いたソフトウェアにこのバグを残したのは私が初めてではないことを教えてください!)

4

3 に答える 3

84

そのハッシュにどのプロパティを持たせたいかによって異なります。たとえば、次のように書くことができます。

public int HashString(string text)
{
    // TODO: Determine nullity policy.

    unchecked
    {
        int hash = 23;
        foreach (char c in text)
        {
            hash = hash * 31 + c;
        }
        return hash;
    }
}

それがハッシュの計算方法であることを文書化する限り、それは有効です。暗号的に安全であるというわけではありませんが、問題なく永続化できます。序数の意味で完全に等しい2つの文字列(つまり、文化的平等が適用されていない、文字ごとにまったく同じ)は、このコードで同じハッシュを生成します。

問題は、文書化されていないハッシュに依存している場合に発生します。つまり、準拠GetHashCode()しているものの、バージョン間で同じであることが保証されていないものstring.GetHashCode()です。

このように独自のハッシュを作成して文書化することは、「この機密情報はMD5(またはその他)でハッシュされます」と言うようなものです。明確に定義されたハッシュである限り、それは問題ありません。

編集:他の回答では、SHA-1やMD5などの暗号化ハッシュを使用することが提案されています。安定性だけでなく暗号化セキュリティの要件があることがわかるまでは、文字列をバイト配列に変換してハッシュするというリグマロールを実行しても意味がありません。もちろん、ハッシュセキュリティ関連の何かに使用されることを意図している場合、業界標準のハッシュはまさにあなたが到達すべきものです。しかし、それは質問のどこにも言及されていませんでした。

于 2011-03-01T13:18:21.870 に答える
22

これは、 .NET が 64 ビット システムの文字列ハッシュ コードを計算する現在の方法の再実装です。これは本物のようにポインターを使用しないGetHashCode()ため、少し遅くなりますが、への内部変更に対する回復力が高くなります。これにより、 Jon Skeet のバージョンstringよりも均等に分散されたハッシュ コードが得られ、辞書でのルックアップ時間が向上する可能性があります。 .

public static class StringExtensionMethods
{
    public static int GetStableHashCode(this string str)
    {
        unchecked
        {
            int hash1 = 5381;
            int hash2 = hash1;

            for(int i = 0; i < str.Length && str[i] != '\0'; i += 2)
            {
                hash1 = ((hash1 << 5) + hash1) ^ str[i];
                if (i == str.Length - 1 || str[i+1] == '\0')
                    break;
                hash2 = ((hash2 << 5) + hash2) ^ str[i+1];
            }

            return hash1 + (hash2*1566083941);
        }
    }
}
于 2016-04-25T16:55:06.087 に答える
-1

答えは、独自のハッシュ関数を作成することです。あなたが投稿した記事へのコメントのリンクをたどることによって、いくつかのソースを見つけることができます。または、元々暗号化を目的とした組み込みのハッシュ関数(MD5、SHA1など)を使用して、すべてのビットを使用しないようにすることもできます。

于 2011-03-01T13:18:02.193 に答える