KeyNotFoundException в C # словаре после изменения значения свойства, на основании чего вычисляется GetHashCode.Зачем? - PullRequest
0 голосов
/ 04 февраля 2019

См. Код ниже.

            static void Main(string[] args)
            {
    // Create Dictionary
                var dict = new Dictionary<TestClass, ValueClass>();

    // Add data to dictionary
                CreateSomeData(dict); 

    // Create a List
                var list = new List<TestClass>();
                foreach(var kv in dict) {
    // Swap property values for each Key
    // For example Key with property value 1 will become 6
    // and 6 will become 1
                    kv.Key.MyProperty = 6 - kv.Key.MyProperty + 1;

    // Add the Key to the List
                    list.Add(kv.Key);
                }

// Try to print dictionary and received KeyNotFoundException.
                foreach (var k in list)
                {
                    Console.WriteLine($"{dict[k].MyProperty} - {k.MyProperty}");
                }
            }



    static void CreateSomeData(Dictionary<TestClass, ValueClass> dictionary) {
        dictionary.Add(new TestClass {MyProperty = 1}, new ValueClass {MyProperty = 1});
        dictionary.Add(new TestClass {MyProperty = 2}, new ValueClass {MyProperty = 2});
        dictionary.Add(new TestClass {MyProperty = 3}, new ValueClass {MyProperty = 3});
        dictionary.Add(new TestClass {MyProperty = 4}, new ValueClass {MyProperty = 4});
        dictionary.Add(new TestClass {MyProperty = 5}, new ValueClass {MyProperty = 5});
        dictionary.Add(new TestClass {MyProperty = 6}, new ValueClass {MyProperty = 6});
    }

Ключ и значение Класс:

namespace HashDictionaryTest
{
    public class TestClass
    {
        public int MyProperty { get; set; }

        public override int GetHashCode() {
            return MyProperty;
        }
    }

}

namespace HashDictionaryTest
{
    public class ValueClass
    {
        public int MyProperty { get; set; }

        public override int GetHashCode() {
            return MyProperty;
        }
    }

}

Я использую ядро ​​dotnet 2.2 в Ubuntu.Я сделал этот тест только из любопытства.Однако, к моему удивлению, я получил KeyNotFoundException .

Я ожидал получить неправильные значения.Однако я получил исключение, как упомянуто выше.

Что я хочу знать, так это то, почему мы получили эту ошибку?Какова лучшая практика создания HashCode, чтобы мы могли избежать таких проблем?

Ответы [ 3 ]

0 голосов
/ 04 февраля 2019

KeyNotFoundException ... Почему?

Основная причина в том, что методы Equals и GetHashCode несовместимы.Эту ситуацию можно исправить, выполнив 2 действия:

  • Переопределить Equals в TestClass
  • Никогда не изменяйте словарь во время итерации
    • Это ключевой объект /значение изменяется

GetHashCode - Equals отключить


TestClass.Equals

Я говорю TestClass, потому что это ключ словаря.Но это относится и к ValueClass.

Класс 'Equals и GetHashCode должны быть согласованными.При переопределении любого, но не обоих, они не согласованы.Мы все знаем, "если вы переопределите Equals, то также переопределите GetHashCode".Мы никогда не переопределяем GetHashCode, но, похоже, это сходит с рук.Услышь меня сейчас и поверь мне в первый раз, когда ты реализуешь IEqualityComparer и IEquatable - всегда переопределяй оба.


Итерирующий словарь

Не добавлять илиудалить элемент, изменить ключ или изменить значение (иногда) во время итерации .


GetHashCode

  • MSDN GetHashCode
    • Не использоватьхеш-код в качестве ключа для извлечения объекта из коллекции ключей.
    • Не проверять на равенство хеш-кодов, чтобы определить, равны ли два объекта

Код OP может делать это не буквально, но, безусловно, виртуально, потому что нет переопределения Equals.

Вот алгоритм аккуратного хэширования от C # полубога Эрика Липпера

0 голосов
/ 05 февраля 2019

Я хочу знать, почему мы получили эту ошибку?

Есть рекомендации для GetHashCode, и есть правила .Если вы нарушаете правила, вы получаете паршивое представление.Если вы нарушаете правила , вещи ломаются.

Вы нарушаете правила .Одно из правил GetHashCode равно , пока объект находится в словаре, его хеш-код не должен изменяться. Другое правило состоит в том, что равные объекты должны иметь одинаковые хеш-коды .

Вы нарушили правила, и поэтому все нарушается.Это твоя вина;не нарушайте правила.

Какова лучшая практика создания HashCode, чтобы мы могли избежать таких проблем?

Список правил и рекомендацийсм .:

https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

0 голосов
/ 04 февраля 2019

Это ожидаемое поведение с вашим кодом.Тогда что не так с вашим кодом?

Посмотрите на свой класс ключей.Вы переопределяете ваш GetHashCode() и, кроме того, вы используете изменяемое значение для вычисления метода GetHashCode() (очень и очень плохо :().

public class TestClass
{
    public int MyProperty { get; set; }

    public override int GetHashCode() {
        return MyProperty;
    }
}

Поиск в реализацииСловарь использует GetHashCode() вставленного объекта. Во время вставки вашего объекта ваш GetHashCode() возвратил некоторое значение, и этот объект был вставлен в некоторый bucket. Однако после того, как вы изменили MyProperty; GetHashCode() делаетне возвращать то же самое значение, поэтому его больше нельзя искать

Здесь происходит поиск

Console.WriteLine($"{dict[k].MyProperty} - {k.MyProperty}");

dict[k] уже изменили MyProperty, поэтому GetHashCode() не возвращает значение при первом добавлении объекта в словарь.

Еще одна важная вещь - помнить, что когда вы переопределяете GetHashCode(), то также переопределяете Equals().тоже!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...