请参见下面的代码。
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。
我希望得到错误的值。不过,我接收到异常如上所述。
我想知道的是,为什么我们得到这个错误?什么是生成散列码,以便我们能够避免这种问题的最佳做法是什么?
我想知道的是,为什么我们得到这个错误?
有对GetHashCode的准则,并有规则。如果违反这些准则,你有个糟糕的表现。如果违反规则,打破东西。
你违反了规则。一个GetHashCode的规则是当一个对象是一个字典,它的散列码不能改变。另一条规则是相等的对象必须具有相同的散列码。
你违反了规则,这样的事情被打破。那是你的错;不违反规则。
什么是生成散列码,以便我们能够避免这种问题的最佳做法是什么?
对于规则和准则的列表,请参阅:
https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/
这是你的代码预期的行为。那么什么是你的你的代码错误?
看看你的主要类别。你是压倒一切的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()
为好。逆是真实的呢!
KeyNotFoundException ...为什么?
蒸出的,核心的原因是Equals
和GetHashCode
方式不一致。这种情况是由做两件事情定:
Equals
在TestClass
GetHashCode
- Equals
断开
GetHashCode()
不完全是。这个问题是一个不断变化的散列码而平等不会改变。
MSDN表示:一个对象的的GetHashCode()方法,必须一致,只要不存在对对象状态没有修改,用于确定对象的System.Object.Equals方法的返回值返回相同的散列码TestClass.Equals
我说TestClass
,因为这是字典键。但是,这也适用于ValueClass
了。
一个类的Equals
和GetHashCode
必须一致。当重写要么但不能同时然后他们并不一致。我们都知道“如果重写Equals
还覆盖GetHashCode
”。我们从来没有覆盖GetHashCode
但似乎摆脱它。现在听我说,相信我,你第一次实现IEqualityComparer
和IEquatable
- 总是会覆盖。
迭代字典
不要添加或删除元素,修改的关键,也不迭代期间修改的值(有时)。
GetHashCode的
操作码可能会因为没有替代的Equals不能从字面上但肯定实际上这样做。