0

私はIDictionary<KeyClass, ValueClass>自分のプロジェクトの1つで使用していました。KeyClassは非常に単純で、整数が 2 つだけ含まれています。

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}
}

次のように辞書から値にアクセスしていました。

var k = new KeyClass(1, 1);
var v = myDictionary[k];

ディクショナリには、および 同様に他の多くのキーKeyClassを含むキーが確かに含まれていました。しかし、私は例外を得ました:ValueA = 1ValueB = 1

The given key is not present in the dictionary.

IComparableクラスで実装しましKeyClassたが、問題は解決しませんでした。私はグーグルでこのCodeProjectの記事を見つけました。クラス実装IEqualityComparer<T>インタフェースを利用するための以下のテクニックについて説明します。私は自分のクラスでそれを定義しました:

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public class EqualityComparer : IEqualityComparer<KeyClass>
    {
        public bool Equals(KeyClass a, KeyClass b)
        {
            return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
        }

        public int GetHashCode(KeyClass k)
        {
            return k.ValueA ^ k.ValueB;
        }
    }
}

IDictionary<KeyClass, ValueClass>ここで、次のように宣言する必要があります。

var d = new Dictionary<KeyClass, ValueClass>(new KeyClass.EqualityComparer());

その後、物事はうまくいきました。

私の質問は、保存環境 (.Net 4.0、Windows 7 デスクトップ アプリ) で同じクラスを使用していて、別のプロジェクトで実装KeyClassせずに作業IEqualityComparet<T>していたのですが、このプロジェクトにクラスを配置した後に動作しなくなったのはなぜですか?

4

2 に答える 2

1

実装する理由はありませんIEqualityComparer<KeyClass>。むしろ、 のメソッドGetHashCodeEqualsメソッドをオーバーライドしてKeyClassください。

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        KeyClass p = obj as KeyClass;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
    }

    public bool Equals(KeyClass p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return this.ValueA == p.ValueA && this.ValueB == b.ValueB;
    }

    public int GetHashCode(KeyClass k)
    {
        return k.ValueA ^ k.ValueB;
    }
}

IEqualityComparer<KeyClass>カスタム比較 (デフォルトの比較とは異なるもの) を行う場合にのみ必要です。ただし、 を制御するのでKeyClass、クラスでデフォルトの比較を指定できます。

このようにすると、ディクショナリを作成するときに比較子を指定する必要がなくなります。つまり、次のように記述できます。

var d = new Dictionary<KeyClass, ValueClass>();

Equals() および演算子 == のガイドラインを参照してください

以前は機能しなかった理由は、おそらく参照型のデフォルトの比較が参照の等価性であるためです。したがって、次のように書くと:

var k = new KeyClass(1, 1);
var v = new ValueClass(..);
d.Add(k, v);

var k1 = new KeyClass(1, 1);
var v1 = d[k1];

「k」と「k1」は同じオブジェクトではないため、「キーが見つかりません」というメッセージが表示されます。しかし、あなたが書いた場合:

var v1 = d[k];

k辞書に保存したキーであるため、値が見つかります。

オーバーライドEqualsすると、この問題が解決します。

于 2012-04-22T00:10:41.360 に答える
0

クラスは参照型です。デフォルトでは、プロパティの値ではなく、クラスの同じインスタンスを指しているかどうかに基づいて、1 つの参照が別の参照と比較されます。

アイテムを追加した元のインスタンスを使用してディクショナリからアイテムを取得できますが、たまたま ValueA と ValueB の値が同じである他のインスタンスでは取得できません。

その動作が必要な場合は、値型 (クラスではなく構造体)、カスタム比較子 (既に行ったように) を使用するか、KeyClass の Equals() および GetHashCode() メソッドをオーバーライドする必要があります。

この場合、構造体を使用するのがおそらく最良の選択肢ですが、ここで読むことができる構造体を使用することの意味を理解することが重要です: 構造体(C# プログラミング ガイド)

于 2012-04-21T23:15:59.360 に答える