我有一个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
时,我也过载了(如MS建议的那样)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;
它返回true。怎么会这样?
编辑2
所以问题最终是因为SortedDictionary
(以及Dictionary
)不是线程安全的。有一个后台线程正在对字典执行某些操作,这些操作似乎触发了集合的一个手段(向集合中添加项目会执行此操作)。同时,当字典迭代遍历值以查找我的密钥时,集合正在被更改,即使它在那里也找不到我的密钥。
对于那些要求代码的人,对不起,我正在调试我继承的应用程序,我没有意识到这是在一个定时的后台线程上进行的。因此,我以为我复制并粘贴了所有相关代码,但我没有意识到在操作集合的所有内容后面还有另一个线程。
答案 0 :(得分:1)
似乎问题最终是因为SortedDictionary
不是线程安全的。有一个后台线程正在对字典执行某些操作(向集合添加项目),这似乎触发了集合的一个手段。同时,当字典试图遍历值以查找我的密钥时,该集合正在被更改和使用,使得枚举器无效,并且即使它在那里也找不到我的密钥。
答案 1 :(得分: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];
代替:
{{1}}
答案 2 :(得分:0)
我怀疑 - 插入后你可能更改密钥的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!
您不应更改在基于哈希的集合中用作键的对象的相等/哈希码注意事项中涉及的属性。理想情况下,更改代码以便无法更改 - 只需UserId
只读,在构造时初始化。
以上肯定 会导致问题 - 但当然,它可能与你看到的问题不一样。