Dictionary.ContainsKey在C#中表现异常

6

我有一个类Column

public class Column
{
    public int Id { get; private set; }
    public string Name { get; private set; }
    public EPersonalCharacteristicType Type { get; private set; }
    public Column MainColumn { get; private set; }
}

我在另一个类中有以下字段:

Dictionary<Column, string> dictionary

在执行过程中,我遇到了这个字典(dictionary)的奇怪行为。当我输入时,它表现得很奇怪。
dictionary.ContainsKey(dictionary.Keys.ToList()[1])

我得到了 false

这怎么可能呢?

更新

我的 Column 类有 GetHashCode 或 Equals 函数。以下是它们的实现:

public bool Equals(Column other)
{
    if (ReferenceEquals(null, other)) return false;
    if (ReferenceEquals(this, other)) return true;
    return other.Id == Id && Equals(other.Name, Name) && Equals(other.Type, Type) && Equals(other.MainColumn, MainColumn) && Equals(other.childColumns, childColumns);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != typeof (Column)) return false;
    return Equals((Column) obj);
}

public override int GetHashCode()
{
    unchecked
    {
        int result = Id;
        result = (result*397) ^ (Name != null ? Name.GetHashCode() : 0);
        result = (result*397) ^ Type.GetHashCode();
        result = (result*397) ^ (MainColumn != null ? MainColumn.GetHashCode() : 0);
        result = (result*397) ^ (childColumns != null ? childColumns.GetHashCode() : 0);
        return result;
    }
}

public static bool operator ==(Column left, Column right)
{
    return Equals(left, right);
}

public static bool operator !=(Column left, Column right)
{
    return !Equals(left, right);
}

2
Column 是否覆盖了 GetHashCode 和/或 Equals 方法? - Jon
只是为了双重确认:你肯定得到的是 false 而不是抛出异常,例如,字典中肯定至少包含 2 个条目([1] 表示列表中的第二个键)? - Kieren Johnstone
3
在LINQPad中进行的简短测试无法重现此问题。我建议您提供一个完整的可工作示例,以便能够重现此行为。 - Daniel Hilgarth
你是否在 Column 上重写了 Equals 和/或 GetHashCode?如果是,你很可能重写不正确,导致了这个问题。 - CodesInChaos
1
列是否不可变?或者至少在添加到字典时不会改变?否则那就是你的问题。 - CodesInChaos
显示剩余2条评论
2个回答

6
这不是一个普遍问题,而是与你的代码有关的特定问题。更新:
字典键的哈希码不能更改。这对于你的Column类来说并非如此,因为只要更改其任何属性,哈希码也会发生变化。文档

只要将对象用作字典中的键,就不能以任何影响其哈希值的方式更改它。

这背后的原因是:字典在添加键时检索和存储键的哈希码。如果键的哈希码在此之后更改,则它与存储的哈希码不同,该对象将不被视为等于最初插入的键。


1
他的所有属性都有私有的setter。因此它很可能是不可变的。但仍然有可能是哈希码随时间变化导致了错误。 - CodesInChaos
是的,我在将列对象添加到字典后进行了更改。 - StuffHappens

2
如果Dictionary<Column, string>是正确的,那么我猜测问题出在Column类型不兼容Dictionary<TKey, TValue>如何检查相等性上 - 这是基于通过GetHashCodeEquals进行比较的 - 而我的猜测是Column类型没有实现这些方法。
将字典的键类型更改为更适合比较和相等性的内容。最好使用从列名派生的字符串。
更新
根据您的代码更新,我猜测自从列被添加到字典中以来,某些关于该列的信息已经发生了变化,导致其“活动”哈希码与其首次添加到字典时不同 - 因此可能会发生任何IdNameTypeMainColumnChildColumns的更改,导致它们的哈希码发生变化。

1
但是除非另有规定,否则字典不应检查引用相等性,他传递到ContainsKey的内容是否与现有键的对象引用相同?特别是如果他没有重写相等方法,检查应该返回true - Jon
1
@Jon - 当我写这个答案时,OP还没有放置带有“Column”实现的代码。我不相信引用相等性会被显式地检查 - 这一切都是通过IEqualityComparer完成的,而在这种情况下会回退到Equals重载。但首先使用GetHashCode - 只有当两个键具有相同的哈希码时才会调用Equals。 - Andras Zoltan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接