0

msdn c#のドキュメントとスタックオーバーフローを検索することで、キーの一意性のチェックと検索Dictionary<T,T>に使用することになっている明確な印象を得ることができます。GetHashCode()

Dictionaryジェネリッククラスは、キーのセットから値のセットへのマッピングを提供します。ディクショナリへの各追加は、値とそれに関連付けられたキーで構成されます。Dictionaryクラスはハッシュテーブルとして実装されているため、キーを使用して値を取得するのは非常に高速で、O(1)に近いです。...取得の速度は、TKeyに指定されたタイプのハッシュアルゴリズムの品質によって異なります。

私は(Unity3Dで)monoを使用しており、作業で奇妙な結果が得られた後、次の実験を行いました。

public class DictionaryTest
{
    public static void TestKeyUniqueness()
    {
    //Test a dictionary of type1
    Dictionary<KeyType1, string> dictionaryType1 = new Dictionary<KeyType1, string>();
    dictionaryType1[new KeyType1(1)] = "Val1";
    if(dictionaryType1.ContainsKey(new KeyType1(1)))
    {
        Debug.Log ("Key in dicType1 was already present"); //This line does NOT print
    }

    //Test a dictionary of type1
    Dictionary<KeyType2, string> dictionaryType2 = new Dictionary<KeyType2, string>();
    dictionaryType2[new KeyType2(1)] = "Val1";
    if(dictionaryType2.ContainsKey(new KeyType2(1)))
    {
        Debug.Log ("Key in dicType2 was already present"); // Only this line prints
    }
  }
}
//This type implements only GetHashCode()
public class KeyType1
{
    private int var1;

    public KeyType1(int v1)
    {
        var1 = v1;
    }

    public override int GetHashCode ()
    {
        return var1;
    }
}
//This type implements both GetHashCode() and Equals(obj), where  Equals uses the hashcode.
public class KeyType2
{
    private int var1;

    public KeyType2(int v1)
    {
        var1 = v1;
    }

    public override int GetHashCode ()
    {
        return var1;
    }

    public override bool Equals (object obj)
    {
        return GetHashCode() == obj.GetHashCode();
    }
}

タイプを使用する場合にのみ、KeyType2キーは等しいと見なされます。私にとってこれは、DictionaryがGetHashCode()ではなくEquals(obj)を使用していることを示しています。

誰かがこれを再現して、意味を解釈するのを手伝ってもらえますか?モノラルでの誤った実装ですか?または私は何かを誤解しましたか。

4

2 に答える 2

2

キーの一意性をチェックするために Dictionary が .GetHashCode() を使用することになっているという明確な印象を受けます

そう思わせた理由は何ですか?GetHashCode一意の値を返しません。

そしてMSDNは明確に言っています:

ディクショナリには、キーが等しいかどうかを判断するための等価実装が必要です。比較パラメーターを受け入れるコンストラクターを使用して、IEqualityComparer ジェネリック インターフェイスの実装を指定できます。実装を指定しない場合、デフォルトの汎用等値比較子 EqualityComparer.Default が使用されます。TKey 型が System.IEquatable ジェネリック インターフェイスを実装している場合、既定の等値比較子はその実装を使用します。

于 2013-03-01T11:05:36.493 に答える
1

これを行う:

public override bool Equals (object obj)
{
    return GetHashCode() == obj.GetHashCode();
}

、、およびインスタンスではないものと等しいKeyType2インスタンスになる可能性があるため、一般的なケースでは間違っています。StringBuilderSomeOtherClassAnythingYouCanImagine

あなたは完全に次のようにする必要があります:

public override bool Equals (object obj)
{
    if (obj is KeyType2) {
        return (obj as KeyType2).var1 == this.var1;
    } else
        return false;
}

オーバーライドしようとしている場合Equals、本質的GetHashCodeに、(クラス MyObject を指定して) 次の点をこの順序で確認する必要があります(逆の方法で行っていました)。

1) 2 つのインスタンスがMyObject等しいのはいつですか? あなたが持っているとしましょう:

public class MyObject {
     public string Name { get; set; }
     public string Address { get; set; }
     public int Age { get; set; }

     public DateTime TimeWhenIBroughtThisInstanceFromTheDatabase { get; set; }
}

そして、このクラスのインスタンスにマップする必要があるデータベースに 1 つのレコードがあります。そして、データベースからレコードを読み取った時間が次の場所に保存されるという規則を作成しますTimeWhenIBroughtThisInstanceFromTheDatabase

MyObject obj1 = DbHelper.ReadFromDatabase( ...some params...);
// you do that at 14:05 and thusly the TimeWhenIBroughtThisInstanceFromTheDatabase
// will be assigned accordingly

// later.. at 14:07 you read the same record into a different instance of MyClass
MyObject obj2 = DbHelper.ReadFromDatabase( ...some params...);
// (the same)

// At 14:09 you ask yourself if the 2 instances are the same
bool theyAre  = obj1.Equals(obj2)

結果をtrueにしますか? 私はあなたが言うだろう。したがって、Equals のオーバーライドは次のようになります。

public class MyObject {
    ...
    public override bool Equals(object obj) {
        if (obj is MyObject) {
            var that = obj as MyObject;
            return (this.Name == that.Name) && 
                   (this.Address == that.Address) &&
                   (this.Age == that.Age);
                   // without the syntactically possible but logically challenged:
                   // && (this.TimeWhenIBroughtThisInstanceFromTheDatabase == 
                   //     that.TimeWhenIBroughtThisInstanceFromTheDatabase)
        } else 
            return false;
    }
    ...
}

2) (実装するEqualsメソッドによって示されるように) 2 つのインスタンスが等しい場合は常に、それらの GetHashCode の結果が同一になるようにします。

int hash1 = obj1.GetHashCode();
int hash2 = obj2.GetHashCode();
bool theseMustBeAlso = hash1 == hash2;

これを行う最も簡単な方法は次のとおりです (サンプル シナリオ)。

   public class MyObject {
    ...
    public override int GetHashCode() {
       int result;
       result = ((this.Name != null) ? this.Name.GetHashCode() : 0) ^
                ((this.Address != null) ? this.Address.GetHashCode() : 0) ^
                this.Age.GetHashCode();
       // without the syntactically possible but logically challenged:
       // ^ this.TimeWhenIBroughtThisInstanceFromTheDatabase.GetHashCode()
    }
    ...
} 

次の点に注意してください: - 文字列は null になる.GetHashCode()可能性があり、NullReferenceException. - ^ (XOR) を使用しました。黄金律 (2 番目) が守られている限り、何を使用してもかまいません。- x ^ 0 == x (x が何であれ)

于 2013-03-01T11:24:51.180 に答える