字典KeyCollection奇怪

时间:2013-03-19 20:30:38

标签: c# .net

我有一个神秘的情况,涉及字典,我从字典中枚举键,但字典不包含它所包含的一些键。

Dictionary<uint, float> dict = GetDictionary(); // Gets values, 6268 pairs
foreach(uint key in dict.Keys)
{
   if (!dict.ContainsKey(key))
      Console.WriteLine("Wat? "+key);
}

以上将打印6268个键中的两个。关于这两个键没有什么特别之处,两个正值都小于Int32.MaxValue(369099203和520093968)。

检查计数显示:

Console.WriteLine(dict.Count);                           // 6268
Console.WriteLine(dict.Keys.Count());                    // 6268
Console.WriteLine(dict.Keys.Count(dict.Keys.Contains));  // 6266 

这是在.NET4.5 CLR下运行的单线程 .NET4代码。字典是一个香草Dictionary<uint, float>,即没有自定义相等比较器。我假设由于uint / int差异而出现哈希问题,但是对于字典的Key集合中返回的所有键,ContainsKey(key)是否应该保证为真?特别是当您只在下面的代码片段中查看KeyCollection对象时,总计数和所包含对象的数量都会关闭,这感觉就像一个奇怪的ICollection行为。

修改:

正如预期的那样,似乎有一个合理的解释:在初始化期间,两个并发线程先前修改了集合 。什么东西&#34;有时会破坏&#34; ,这是一个线程问题,果然。从几个线程访问一个字典显然会扰乱内部状态,使其在其生命周期的剩余时间内保持半功能状态,但不会导致任何异常。

我要切换到并发字典,可能会删除这个问题。感谢。

4 个答案:

答案 0 :(得分:1)

我没有足够的代表评论 - 但我确实试图重现你的问题无济于事。我建议您发布GetDictionary()的工作方式,我建议不要迭代这样的字典,而是在下面做,看看是否有解决方法:

foreach (KeyValuePair<uint, float> pair in dict)
    Console.WriteLine("[" + pair.Key + "]=" + pair.Value);

答案 1 :(得分:1)

构造字典时,GetDictionary()是否有可能添加自定义键相等比较器?如果是这样,问题可能与比较器实现有关。

答案 2 :(得分:0)

重要说明:在本文中,当我引用GetHashCode时,我指的是IEqualityComparer<T>.GetHashCode的结果。默认情况下,字典将使用EqualityComparer<T>.Default,这将返回在密钥本身上调用GetHashCode的结果。但是,您可以在创建字典时使用IEqualityComparer<T>来提供特定的实现,以使用不同的行为。

如果键的GetHashCode结果在将值添加到字典的时间与枚举键的位置之间发生变化,则会发生这种情况。枚举键时,它返回数组中所有填充“桶”的键。但是,当您查找特定的键时,它会从密钥的GetHashCode结果重新计算预期的存储桶。如果哈希码发生了变化,那么字典桶中键/值对的实际位置和预期位置可能不再相同,在这种情况下Contains将返回false。

在将值添加到密钥的字典中之后,您应确保字典中的键的GetHashCode结果不会更改。

答案 3 :(得分:0)

我也遇到过与System.Uri类似的怪异行为。

它转变为存储在字典中的密钥与我用于查找的密钥之间的体系结构不匹配。特别是,存储在字典中的Uri是32位,而我正在寻找64位的。显然,由于 GetHashcode()未被授予在不同体系结构之间相等,因此字典无法与键匹配。