3

我正在存储一个二维数组,它将向量之间的距离矩阵表示为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()and 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可以互换的。所以对于两个字符串and ,必须相等。这就是为什么我用乘法实现的原因,因为must equal 。Group1Group2xyDistanceCell("x", "y")DistanceCell("y", "x")GetHashCode()DistanceCell("x", "y").GetHashCode()DistanceCell("y", "x").GetHashCode()

我遇到的问题是它大约 90% 的时间都可以正常工作,但在其余时间它会抛出 aKeyNotFoundException或 a 。NullReferenceException前者在从字典中获取键时被抛出,后者在我使用循环遍历字典foreach并检索一个空键时被抛出,然后它会尝试调用该键Equals()。我怀疑这与我的GetHashCode()实施中的错误有关,但我并不积极。另请注意,由于我的算法的性质,当我检查它时,永远不应该存在字典中不存在密钥的情况。该算法每次执行都采用相同的路径。

更新

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

感谢每一个人的帮助!很抱歉,这完全是我的错。

4

3 回答 3

4

看看Eric Lippert的这篇博文

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

我可能错了,但值得测试。

于 2013-01-20T20:18:27.957 回答
2

我相信你缺少的是这个答案 Using an object as a generic Dictionary key

你可能没有说你实现了 IEquatable 接口

于 2013-01-20T20:03:27.283 回答
0

对我来说,您的代码看起来是正确的。(它可能是(微)优化的,但它看起来好像在所有情况下都是正确的。)你能提供一些你创建和使用的代码Dictionary<DistanceCell, double>吗?问题可能在于您没有向我们展示的代码。

于 2013-01-20T20:23:57.090 回答