2013-08-20 3 views
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 

Как я парить над словарем (после ошибки) с отладчиком, я могу ясно видеть один и тот же ключ, который я пытался ссылаться как на самом деле содержится в словаре. Я заполняю словарь, используя ReadOnlyCollection из MyObjects. Может, там что-то странное? Я попробовал переопределить оператора == и методы Equals, чтобы получить явное сравнение, которое я хотел, но такой удачи не было. Это действительно не имеет значения, так как я фактически получаю ключ непосредственно от Dictionary, затем запрашивая Dictionary, используя тот же ключ. Я не могу понять, что вызывает это. Кто-нибудь видел это поведение?

РЕДАКТИРОВАТЬ 1

В наиважнейшей Equals Я также перегружен (как МС рекомендует) 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; 

он возвращает истину , Как это может быть?

EDIT 2 Таким образом, проблема закончилась тем, что из-за SortedDictionaryDictionary а) не поточно-. Был фоновый поток, который выполнял некоторые операции над словарем, который, кажется, запускает курорт коллекции (добавление элементов в коллекцию сделает это). В то же время, когда словарь повторял значения, чтобы найти мой ключ, коллекция менялась, и она не находила мой ключ, даже если он был там.

Извините за всех, кто попросил ввести код на этот, я в настоящее время отлаживаю приложение, которое унаследовал, и я не понимал, что это происходит по временному фоновому потоку. Как таковой, я думал, что я скопировал и вставил весь соответствующий код, но я не понимал, что за всем, управляющим коллекцией, существует еще один поток.

+1

Вам необходимо * переопределить * Equals (и GetHashCode), хотя вы также можете перегрузить его. Пожалуйста, покажите свой класс - вернее, короткую, но полную программу, демонстрирующую проблему. –

+0

Чтобы быть ясным: вы наблюдали это впервые без переопределения/перегрузки Equals/==/GetHashCode? Потому что ошибочная реализация этого будет легким ответом. –

+0

@JonSkeet - Ты прав, я неправильно напечатал. Я использовал _override_ методы 'Equals' (и' GetHashCode'). Я добавил соответствующий код. –

ответ

0

похоже, что проблема закончилась тем, что SortedDictionary не является потокобезопасным. Был фоновый поток, который выполнял некоторые операции над словарем (добавление элементов в коллекцию), который, кажется, запускает курорт коллекции. В то же время, когда словарь пытался перебирать значения, чтобы найти мой ключ, коллекция менялась и прибегала к ней, делая перечислитель недействительным, и он не нашел мой ключ, даже если он был там.

0

В дополнение к перегрузке == и Equals убедитесь, что вы переопределили GetHashCode с подходящей функцией хэша. В частности, см этой спецификации из документации:

  • Если два объекта сравнивается как равным, метод GetHashCode для каждого объекта должен возвращать одинаковое значение. Однако, если два объекта не сравнить как равные, методы GetHashCode для двух объектов не должны возвращать разные значения.
  • Метод GetHashCode для объекта должен последовательно возвращать один и тот же хэш-код, если не существует модификации состояния объекта , которая определяет возвращаемое значение метода Equals объекта. Примечание , что это верно только для текущего выполнения приложения, и что другой хеш-код может быть возвращен, если приложение снова запустится.
  • Для обеспечения максимальной производительности хеш-функция должна генерировать равномерное распределение для всех входных данных, включая ввод, который сильно кластеризован. Импликация заключается в том, что небольшие изменения состояния объекта должны привести к большим изменениям в результате хеш-кода для наилучшего хэша производительности таблицы.
  • Хеш-функции должны быть недорогими для вычисления.
  • Метод GetHashCode не должен генерировать исключения.

Я согласен с Jon Skeet's suspicion, что вы как-то невольно модифицирующих UserID свойство после того, как он добавляется в качестве ключа. Но так как единственное свойство, которое очень важно для проверки равенства в MyObject является UserID (и, следовательно, это единственное свойство, что Dictionary заботится о), я бы рекомендовал рефакторинга код, чтобы использовать простой Dictionary<ulong, IMyInterface> вместо:

Dictionary<ulong, IMyInterface> dict = new Dictionary<string, IMyInterface>(); 
ulong userID = dict.Keys.First(); 
var value = dict[userID]; 
+0

Я сделал, обновляя свой вопрос, чтобы отразить это ... –

+0

@JoelB Пожалуйста, покажите свою реализацию '==', 'Equals' и' GetHashCode'. Мы можем обнаружить ошибку. –

+0

Добавлен код .. спасибо! –

0

I есть подозрение - возможно, что вы в меняете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! 

Вы не должны изменять свойства, связанные с соображениями равенства/хэш-кода для объекта, который используется в качестве ключа в хэш на основе коллекции. В идеале измените свой код так, чтобы это не могло быть изменено - make UserId readonly, инициализировано по строительству.

Вышеуказанный определенно будет вызвать проблему - но, возможно, это не то же самое, что проблема, которую вы видите, конечно.

+0

Точка взята, и я обязательно включу защиту от этого, если я сохраню свою переопределенную реализацию «GetHashCode». В этом случае это не проблема с тех пор, как я столкнулся с проблемой, прежде чем переопределить «GetHashCode» –

+0

@JoelB: Ну, если бы вы переопределили «Равно» в этот момент? Это единственное объяснение, о котором я могу думать, но поскольку мы в настоящее время не имеем возможности воспроизвести проблему, мы оставляем догадки. Опять же, если вы можете предоставить способ воспроизвести это, мы с большей вероятностью можем вам помочь. –

Смежные вопросы