私はC#で構造を持っています:
_public struct UserInfo
{
public string str1
{
get;
set;
}
public string str2
{
get;
set;
}
}
_
唯一のルールは、UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))
この構造のGetHashCode関数をオーバーライドする方法は?
ハッシュ関数には次のプロパティが必要です。
- 2つのオブジェクトが等しいと比較される場合、各オブジェクトの
GetHashCode
メソッドは同じ値を返す必要があります。ただし、2つのオブジェクトが等しく比較されない場合、2つのオブジェクトのGetHashCode
メソッドは異なる値を返す必要はありません。- オブジェクトの
GetHashCode
メソッドは、オブジェクトのEquals
メソッドの戻り値を決定するオブジェクト状態に変更がない限り、常に同じハッシュコードを返す必要があります。これは、アプリケーションの現在の実行にのみ当てはまることに注意してください。アプリケーションを再度実行すると、異なるハッシュコードが返される可能性があります。- 最高のパフォーマンスを得るには、ハッシュ関数はすべての入力に対してランダムな分布を生成する必要があります。
それを正しい方法で考慮することは:
return str1.GetHashCode() ^ str2.GetHashCode()
^
は、他の可換演算に置き換えることができます
Jon Skeetの答え を参照-^
は良くありません。しばしば衝突ハッシュを生成します!
public override int GetHashCode()
{
unchecked
{
return (str1 ?? String.Empty).GetHashCode() +
(str2 ?? String.Empty).GetHashCode();
}
}
( 'AA'、 'BB')と( 'BB'、 'AA')を明示的に同じにしたいが、(^ 「AA」、「AA」)および(「BB」、「BB」)が同じ(または、すべての等しいペア).
Nullの場合、これは既知の定数をすぐに返すのではなく、空の文字列で 'GetHashCode()'を実行するため、このソリューションでは「可能な限り高速」のルールが完全に順守されていませんが、明示的に測定しなくても多くのヌルを期待しない限り、違いが心配するほど大きくないという推測を危険にさらすために。
一般的なルールとして、クラスのハッシュコードを生成する簡単な方法は、ハッシュコードの生成に参加できるすべてのデータフィールドをXORすることです(他の人が指摘したようにnullをチェックするよう注意してください)。これは、UserInfo( "AA"、 "BB")とUserInfo( "BB"、 "AA")のハッシュコードが同じであるという(人工的?)要件も満たしています。
クラスの使用について推測できる場合は、おそらくハッシュ関数を改善できます。たとえば、str1とstr2が同じであることが一般的である場合、XORは適切な選択ではない場合があります。しかし、str1とstr2が、たとえば姓と名を表す場合、XORがおそらく適切な選択です。
これは明らかに実世界の例であることを意図していないが、それを指摘する価値があるかもしれない:-これはおそらく構造体の使用の悪い例です:構造体は通常値セマンティクスを持つべきであり、ここにケース。 -セッターを使用してプロパティを使用してハッシュコードを生成することも問題の原因です。
簡単な一般方法はこれを行うことです:
return string.Format("{0}/{1}", str1, str2).GetHashCode();
厳密なパフォーマンス要件がない限り、これは私が考えることができる最も簡単な方法であり、複合キーが必要なときにこの方法を頻繁に使用します。 null
のケースをうまく処理し、(一般的に)ハッシュ衝突を(m)引き起こしません。文字列に「/」が必要な場合は、予期しない別のセパレータを選択してください。
ReSharperが提案している線に沿って進むと:
public int GetHashCode()
{
unchecked
{
int hashCode;
// String properties
hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);
// int properties
hashCode = (hashCode * 397) ^ intProperty;
return hashCode;
}
}
397は、結果変数がオーバーフローし、ハッシュのビットをいくらか混合するのに十分なサイズの素数であり、ハッシュコードのより良い分布を提供します。それ以外の場合、397には同じ大きさの他の素数と区別する特別なものはありません。
public override int GetHashCode()
{
unchecked
{
return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);
}
}
ああ、ゲイリー・シュトラーが指摘したように:
return str1.GetHashCode() + str2.GetHashCode();
オーバーフローする可能性があります。 Artemが提案したように長くキャストするか、未チェックのキーワードでステートメントを囲むことができます。
return unchecked(str1.GetHashCode() + str2.GetHashCode());
これを試してください:
(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
多くの可能性。例えば。
return str1.GetHashCode() ^ str1.GetHashCode()
おそらくstr1.GetHashCode()+ str2.GetHashCode()のようなものでしょうか?または(str1.GetHashCode()+ str2.GetHashCode())/ 2?この方法では、str1とstr2がスワップされるかどうかに関係なく同じになります。
それらをソートしてから連結します。
return((str1.CompareTo(str2)<1)?str1 + str2:str2 + str1) .GetHashCode();
GetHashCodeの結果は次のようになります。
それらを念頭に置いて、私は次のようなものに行きます:
if (str1 == null)
if (str2 == null)
return 0;
else
return str2.GetHashCode();
else
if (str2 == null)
return str1.GetHashCode();
else
return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();
編集: nullを忘れました。コードが修正されました。