5

我有这样的代码:

if (CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key))
   CounterForEachRelatedTagDict[tag.Key] += tag.Value;

IF语句是否有可能返回true,同时CounterForEachRelatedTagDict[tag.Key]返回“给定键不在字典中”错误?tag是一个KeyValuePair<MyClass,int>

CounterForEachRelatedTagDict像这样启动:

Dictionary<MyClass, int> CounterForEachRelatedTagDict = new Dictionary<MyType, int>();

MyClass是这样的

public class MyClass
{
    public string name {get;set;}
    public Guid Id { get; set; }
    ...
}

在我看来这几乎是不合理的......

4

3 回答 3

4

问题是您的EqualGetHashCode方法不同步MyType

当您使用时,CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key)您正在通过所有键执行线性搜索,Equals以比较您正在搜索的每个键。

当您使用ContainsKeyin Dictionary、索引器或许多其他方法之一来查找密钥时,您首先使用该密钥对密钥进行哈希处理GetHashCode,然后它仅用于Equals查找该存储桶中的哪些(希望很少的对象)是相同的。

发生的事情是您有两个对象first.Equals(second)返回 true,但GetHashCode返回两个不同的值。非常重要的是,当使用对象作为 a 中的键时Dictionary,任何两个返回的对象Equalstrue必须返回相同的整数GetHashCode。理想情况下,不同的对象应该尽可能返回不同的哈希码,但这并不总是可能的(具有相同哈希码的不同对象称为“碰撞”)。

请注意,这种查找键的方法,虽然它确实迫使您确保所有用作键的对象都具有合理的实现GetHashCode(来自的默认实现object很少合适),但该算法*非常*高效(使用高效的散列算法),即是什么让它值得。使用ContainsKey,或字典的索引器,比遍历每个键并比较它要快得多,这是您的Select代码需要做的,以避免使用GetHashCode.

所以,要回答你的问题,是的,很有可能CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key)找到一个项目,而索引器却不能。

于 2012-12-03T18:41:56.113 回答
2

第一:您可以使用该ContainsKey方法而不是那个 Linq Query。

第二:您必须覆盖GetHashCodeand Equalsfor MyType。这就是 Dictionary 查找和比较键的方式。

查看这些类似的问题:Dictionary.ContainsKey return False, but a want True , Using an object as a generic Dictionary key

于 2012-12-03T18:37:34.747 回答
1

要将您的类型用作字典键,您应该重写两种方法:GetHashCodeEquals.

默认情况下(如果您不覆盖GetHashCode)您类型的每个对象(即使具有相同的字段值)都将返回唯一值。这意味着您将只能找到您将放入字典中的完全相同的“参考”。考虑以下两种类型:MyType1不覆盖GetHashCodeandEquals和 MyType2 :

class MyType1
{
  public MyType1(int id, string name) {Id = id; Name = name;}
  public int Id {get; private set;}
  public string Name {get; private set;}
}


internal class MyType2
{
    public MyType2(int id, string name)
    {
        Id = id;
        Name = name;
    }

    public int Id { get; private set; }
    public string Name { get; private set; }

    bool Equals(MyType2 other)
    {
        return Id == other.Id && string.Equals(Name, other.Name);
    }

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

    public override int GetHashCode()
    {
        unchecked
        {
            return (Id*397) ^ Name.GetHashCode();
        }
    }
}

var d1 = new Dictionary<MyType1, int>();
d1[new MyType1(1, "1")] = 1;
d1[new MyType1(1, "1")]++; // will throw withKeyNotFoundException

var d2 = new Dictionary<MyType2, int>();
d1[new MyType2(1, "1")] = 1;
d1[new MyType2(1, "1")]++; // Ok, we'll find appropriate record in dictionary
于 2012-12-03T18:47:29.837 回答