简体   繁体   English

使用自定义类作为键时,“给定键不存在于字典中”错误

[英]“the given key was not present in the dictionary” error when using a self-defined class as key

I've got code like this: 我有这样的代码:

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

Is it possible that the IF statement returns true, at the same time CounterForEachRelatedTagDict[tag.Key] returns "the given key was not present in the dictionary" error? 是否有可能IF语句返回true,同时CounterForEachRelatedTagDict[tag.Key]返回“给定的键不存在于字典中”错误? tag is a KeyValuePair<MyClass,int> . tagKeyValuePair<MyClass,int>

CounterForEachRelatedTagDict is initiated like this: CounterForEachRelatedTagDict的启动方式如下:

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

MyClass is like this MyClass就是这样的

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

It seems almost unreasonable to me... 这似乎对我来说几乎是不合理的......

The problem is that your Equal and GetHashCode methods are out of sync for MyType . 问题是您的EqualGetHashCode方法与MyType不同步。

When you use CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key) you're performing a linear search through all of the keys using Equals to compare what you're searching for to each key. 当您使用CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key)您正在使用Equals对所有键执行线性搜索,以比较您要搜索的每个键的内容。

When you use ContainsKey in Dictionary , the indexer, or one of a number of other methods for finding a key you first hash the key using GetHashCode and then it only uses Equals to find which of the (hopefully very few objects) are identical within that bucket. 当您在Dictionary使用ContainsKey ,索引器或用于查找键的许多其他方法之一时,您首先使用GetHashCode对键进行散列,然后它仅使用Equals来查找哪个(希望极少数对象)在其中相同桶。

What's happening is that you have two object for which first.Equals(second) returns true, but for which GetHashCode returns two different values. 发生的事情是你有两个对象, first.Equals(second)返回true,但GetHashCode返回两个不同的值。 It's very important that, when using objects as keys in a Dictionary , any two objects for which Equals returns true must also return the same integer for GetHashCode . 非常重要的是,当在Dictionary使用对象作为键时, Equals返回true任何两个对象也必须为GetHashCode返回相同的整数。 Ideally different objects should return different hash codes whenever possible, but it's not always possible (different objects with the same hash code are called "collisions"). 理想情况下,不同的对象应尽可能返回不同的哈希码,但并不总是可能(具有相同哈希码的不同对象称为“冲突”)。

Note that this method of finding keys, while it does force you to ensure all objects used as keys have sensible implementations of GetHashCode (the default implementation that comes from object is rarely appropriate) this algorithm is * extraordinary* efficient (with efficient hashing algorithms) which is what makes it worthwhile. 请注意,这种查找键的方法虽然确实会强制您确保用作键的所有对象都具有合理的GetHashCode实现(来自object的默认实现很少合适),但这种算法非常高效(使用高效的哈希算法)这是值得的。 Using ContainsKey , or the indexer of the dictionary, is much, much faster than going through each key and comparing it, which is what your Select code needs to do to avoid using GetHashCode . 使用ContainsKey或字典的索引器比通过每个键并比较它要快得多,这是您的Select代码需要做的以避免使用GetHashCode

So, to answer your question, yes, it's quite possible for CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key) to find an item while the indexer can't. 所以,回答你的问题,是的, CounterForEachRelatedTagDict.Select(x => x.Key).Contains(tag.Key)很有可能找到一个项目而索引器不能。

First:You can use the ContainsKey method instead of that Linq Query. 第一:您可以使用ContainsKey方法而不是Linq Query。

Second: You must override the GetHashCode and Equals for MyType . 第二:你必须重写GetHashCodeEqualsMyType that's the way that Dictionary look up and compare the keys. 这就是Dictionary查找和比较键的方式。

Check out these similar questions: Dictionary.ContainsKey return False, but a want True , Using an object as a generic Dictionary key 看看这些类似的问题: Dictionary.ContainsKey返回False,但是想要True使用对象作为通用的Dictionary键

To use your type as a dictionary key you should override two methods: GetHashCode and Equals . 要将类型用作字典键,您应该重写两个方法: GetHashCodeEquals

By default (if you'll not override GetHashCode ) every object of your type (even with the same field values) will return unique value. 默认情况下(如果您不会覆盖GetHashCode )您的类型的每个对象(即使具有相同的字段值)将返回唯一值。 This means that you'll be able to find only exactly the same "reference" that you'll put into your dictionary. 这意味着您将只能找到与您的字典完全相同的“参考”。 Consider following two types: MyType1 that not overrides GetHashCode and Equals , and MyType2 that do: 考虑以下两种类型: MyType1不是重写GetHashCodeEquals ,并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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM