C# 字典在键存在时抛出 KeyNotFoundException

标签 c# dictionary nullreferenceexception keynotfoundexception

我正在存储一个二维数组,它表示向量之间的距离矩阵作为 Dictionary<DistanceCell, double> .我的实现 DistanceCell有两个字符串字段代表被比较的向量。

class DistanceCell
{
    public string Group1 { get; private set; }
    public string Group2 { get; private set; }

    public DistanceCell(string group1, string group2)
    {
        if (group1 == null)
        {
            throw new ArgumentNullException("group1");
        }

        if (group2 == null)
        {
            throw new ArgumentNullException("group2");
        }

        this.Group1 = group1;
        this.Group2 = group2;
    }
}

因为我使用这个类作为键,所以我覆盖了 Equals()GetHashCode() :

public override bool Equals(object obj)
{
    // False if the object is null
    if (obj == null)
    {
        return false;
    }

    // Try casting to a DistanceCell. If it fails, return false;
    DistanceCell cell = obj as DistanceCell;
    if (cell == null)
    {
        return false;
    }

    return (this.Group1 == cell.Group1 && this.Group2 == cell.Group2) 
           || (this.Group1 == cell.Group2 && this.Group2 == cell.Group1);
}

public bool Equals(DistanceCell cell)
{
    if (cell == null)
    {
        return false;
    }

    return (this.Group1 == cell.Group1 && this.Group2 == cell.Group2) 
           || (this.Group1 == cell.Group2 && this.Group2 == cell.Group1);
}

public static bool operator ==(DistanceCell a, DistanceCell b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If either is null, return false.
    // Cast a and b to objects to check for null to avoid calling this operator method
    // and causing an infinite loop.
    if ((object)a == null || (object)b == null)
    {
        return false;
    }

    return (a.Group1 == b.Group1 && a.Group2 == b.Group2) 
           || (a.Group1 == b.Group2 && a.Group2 == b.Group1);
}

public static bool operator !=(DistanceCell a, DistanceCell b)
{
    return !(a == b);
}

public override int GetHashCode()
{
    int hash;
    unchecked
    {
        hash = Group1.GetHashCode() * Group2.GetHashCode();
    }

    return hash;
}

如您所见,DistanceCell 的要求之一是那个Group1Group2是可以互换的。所以对于两个字符串 xy , DistanceCell("x", "y")必须等于 DistanceCell("y", "x") .这就是我实现 GetHashCode() 的原因乘法因为DistanceCell("x", "y").GetHashCode()必须等于 DistanceCell("y", "x").GetHashCode() .

我遇到的问题是它在大约 90% 的时间内都可以正常工作,但会抛出 KeyNotFoundExceptionNullReferenceException剩下的时间。前者在从字典中获取键时抛出,后者在我使用 foreach 遍历字典时抛出。循环并检索一个为 null 的键,然后尝试调用 Equals()上。我怀疑这与我的 GetHashCode() 中的错误有关实现,但我不是积极的。另请注意,由于算法的性质,当我检查字典时,绝不会出现 key 不存在的情况。该算法每次执行都采用相同的路径。

更新

我只是想告诉大家这个问题已经解决了。事实证明,它与我的 Equals() 或 GetHashCode() 实现无关。我做了一些广泛的调试,发现我得到 KeyNotFoundException 的原因是因为字典中根本不存在该键,这很奇怪,因为我确信它被添加了。问题是我用多个线程将键添加到字典中,据此,c# Dictionary 类不是线程安全的。所以 Add() 失败的时机一定是完美的,因此 key 从未被添加到字典中。我相信这也可以解释 foreach 循环是如何偶尔产生一个空键的。来自多个线程的 Add() 一定弄乱了字典中的一些内部结构并引入了一个空键。

感谢大家的帮助!很抱歉,这完全是我的错。

最佳答案

看看this blog post作者:埃里克·利珀特

它表示即使更改对象的内容,GetHashCode 的结果也永远不会改变。原因是 Dictionary 使用桶来加快索引速度。更改 GetHasCode 结果后,Dictionary 将无法为您的对象找到正确的存储桶,这可能导致“找不到键”

我可能是错的,但值得测试。

关于C# 字典在键存在时抛出 KeyNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14428668/

相关文章:

c# - 什么是NullReferenceException,如何解决?

c# - 如何使用 LINQ to XML 连接具有相同名称值的所有子元素

c# - 对象中的 EF6 AddOrUpdate 对象

dictionary - Golang 多个计时器与 map+channel+mutex

python - 压缩大小不等的列表(值多于键)以形成统一的字典

c# - 字典 trygetvalue null

c# - EmguCV - 人脸识别 - 使用来自 Microsoft Access 数据库的训练集时出现 'Object reference not set' 异常

c# - WPF:样式化文本框不显示光标

c# - 线程中的异常处理,联系 WCF 服务

ios - 如何在字典数组中的内部数组上应用过滤器?