1

私は次のSortedDictionaryように宣言しました:

SortedDictionary<MyObject,IMyInterface> dict = new SortedDictionary<MyObject,IMyInterface>();

値が入力されたときに、辞書からキーを取得してすぐに参照しようとすると、次のようになりますKeyNotFoundException

MyObject myObj = dict.Keys.First();
var value = dict[myObj];     // This line throws a KeyNotFoundException

デバッガーで (エラーの後) 辞書にカーソルを合わせると、参照しようとしたのと同じキーが実際に辞書に含まれていることがはっきりとわかります。ReadOnlyCollectionofを使用して辞書にデータを入力していMyObjectsます。そこで何かおかしなことが起きているのではないでしょうか?==演算子とメソッドをオーバーライドしてEquals、必要な明示的な比較を取得しようとしましたが、そのような運はありませんでした。実際には から直接キーを取得し、同じキーを使用してDictionaryクエリを実行しているので、それは問題ではありません。Dictionary何が原因なのかわかりません。誰もこの行動を見たことがありますか?

編集1

オーバーライドEqualsでは、(MS が推奨するように) オーバーロードも行いGetHashCodeました。MyObject興味のある人のための実装は次のとおりです。

public class MyObject
{
public string UserName { get; set;}
public UInt64 UserID  { get; set;}

    public override bool Equals(object obj)
    {
        if (obj == null || GetType()!= obj.GetType())
        {
            return false;
        }

        // Return true if the fields match:
        return this.Equals((MyObject)obj);
    }

    public bool Equals(MyObject other)
    {
        // Return true if the fields match
        return this.UserID == other.UserID;
    }

    public override int GetHashCode()
    {
        return (int)this.UserID;
    }


public static bool operator ==( MyObject a, MyObject b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.UserID == b.UserID
}

public static bool operator !=( MyObject a, MyObject b)
{
    return !(a == b);
}
}

デバッグから気付いたのKeyNotFoundExceptionは、式のクイック ウォッチを (がスローされた後に)追加すると、次のようになることです。

dict.ElementAt(0).Key == value;

true を返します。どうすればいいの?

EDIT 2 したがって、問題はSortedDictionary(およびDictionary同様に)スレッドセーフではないためです。ディクショナリに対していくつかの操作を実行しているバックグラウンド スレッドがあり、それがコレクションの再利用を引き起こしているようです (コレクションに項目を追加すると、これが実行されます)。同時に、ディクショナリが値を反復処理してキーを見つけたときに、コレクションが変更されていて、そこにあるのにキーを見つけられませんでした。

このコードを求めてくれた皆さん、申し訳ありません。現在、継承したアプリケーションをデバッグしていますが、これが時限のバックグラウンド スレッドで行われていることに気づきませんでした。そのため、関連するすべてのコードをコピーして貼り付けたと思っていましたが、コレクションを操作するすべての背後で別のスレッドが実行されていることに気付きませんでした。

4

3 に答える 3

1

SortedDictionaryスレッドセーフではないため、問題が発生したようです。ディクショナリでいくつかの操作を実行している (コレクションに項目を追加する) バックグラウンド スレッドがあり、コレクションの再起動をトリガーしているようです。同時に、ディクショナリが値を反復処理してキーを見つけようとしたときに、コレクションが変更されて再ソートされ、列挙子が無効になり、そこにあるにもかかわらずキーが見つかりませんでした。

于 2013-08-20T20:59:41.967 に答える
0

疑いがあります -挿入後にキーの を変更している可能性があります。UserIDたとえば、これは問題を示しています。

var key = new MyObject { UserId = 10 };
var dictionary = new Dictionary<MyObject, string>();
dictionary[key] = "foo";

key.UserId = 20; // This will change the hash code

var value = dict[key]; // Bang!

ハッシュベースのコレクションでキーとして使用されているオブジェクトの等値/ハッシュ コードの考慮事項に関連するプロパティを変更しないでください。理想的には、これを変更できないようにコードを変更します -UserId読み取り専用にし、構築時に初期化します。

上記は間違いなく問題引き起こしますが、もちろん、あなたが見ている問題と同じではない可能性もあります。

于 2013-08-20T17:29:33.360 に答える
0

==とのオーバーロードに加えてEquals、必ずGetHashCode適切なハッシュ関数でオーバーライドしてください。特に、ドキュメントからこの仕様を参照してください。

  • 2 つのオブジェクトを比較して等しい場合、GetHashCode各オブジェクトのメソッドは同じ値を返す必要があります。ただし、2 つのオブジェクトが等しくない場合、2 つのオブジェクトのGetHashCodeメソッドは異なる値を返す必要はありません。
  • オブジェクトのGetHashCodeメソッドは、オブジェクトの Equals メソッドの戻り値を決定するオブジェクトの状態に変更がない限り、一貫して同じハッシュ コードを返す必要があります。これはアプリケーションの現在の実行にのみ当てはまり、アプリケーションが再度実行されると別のハッシュ コードが返される可能性があることに注意してください。
  • 最高のパフォーマンスを得るには、ハッシュ関数は、非常にクラスター化された入力を含む、すべての入力に対して均等な分布を生成する必要があります。これは、オブジェクトの状態を少し変更すると、結果として得られるハッシュ コードが大幅に変更され、最高のハッシュ テーブル パフォーマンスが得られることを意味します。
  • ハッシュ関数は低コストで計算できる必要があります。
  • メソッドは例外をスローしてはGetHashCodeなりません。

プロパティがキーとして追加された後、何らかの形で意図せずにプロパティを変更しているというJon Skeetの疑いに同意します。UserIDしかし、等価性をテストするために重要な唯一のプロパティMyObjectUserID(したがって、それがDictionary気にする唯一のプロパティです) であるため、Dictionary<ulong, IMyInterface>代わりに単純なものを使用するようにコードをリファクタリングすることをお勧めします。

Dictionary<ulong, IMyInterface> dict = new Dictionary<string, IMyInterface>();
ulong userID = dict.Keys.First();
var value = dict[userID];
于 2013-08-20T16:43:36.213 に答える