KeyNotFoundException在C#中词典依据是什么时,GetHashCode的计算改变属性值后。为什么?

问题描述 投票:2回答:3

请参见下面的代码。

            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;
        }
    }

}

我使用Ubuntu上的DOTNET核心2.2。我做了这个测试出来的只是好奇。然而,出乎我的意料,我得到了KeyNotFoundException。

我希望得到错误的值。不过,我接收到异常如上所述。

我想知道的是,为什么我们得到这个错误?什么是生成散列码,以便我们能够避免这种问题的最佳做法是什么?

c# dictionary hash
3个回答
5
投票

我想知道的是,为什么我们得到这个错误?

有对GetHashCode的准则,并有规则。如果违反这些准则,你有个糟糕的表现。如果违反规则,打破东西。

你违反了规则。一个GetHashCode的规则是当一个对象是一个字典,它的散列码不能改变。另一条规则是相等的对象必须具有相同的散列码。

你违反了规则,这样的事情被打破。那是你的错;不违反规则。

什么是生成散列码,以便我们能够避免这种问题的最佳做法是什么?

对于规则和准则的列表,请参阅:

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


4
投票

这是你的代码预期的行为。那么什么是你的你的代码错误?

看看你的主要类别。你是压倒一切的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()为好。逆是真实的呢!


2
投票

KeyNotFoundException ...为什么?

蒸出的,核心的原因是EqualsGetHashCode方式不一致。这种情况是由做两件事情定:

  • 覆盖EqualsTestClass
  • 切勿改装迭代过程中的字典 这是该键对象/值被修改

GetHashCode - Equals断开


TestClass.Equals

我说TestClass,因为这是字典键。但是,这也适用于ValueClass了。

一个类的EqualsGetHashCode必须一致。当重写要么但不能同时然后他们并不一致。我们都知道“如果重写Equals还覆盖GetHashCode”。我们从来没有覆盖GetHashCode但似乎摆脱它。现在听我说,相信我,你第一次实现IEqualityComparerIEquatable - 总是会覆盖。


迭代字典

不要添加或删除元素,修改的关键,也不迭代期间修改的值(有时)。


GetHashCode的

  • MSDN GetHashCode 不要使用哈希码为重点,以从一个键集合中检索对象。 不要测试的散列码相等,以确定两个对象是否相等

操作码可能会因为没有替代的Equals不能从字面上但肯定实际上这样做。

Here is a neat hash algorithm from C# demi god Eric Lipper

© www.soinside.com 2019 - 2024. All rights reserved.