C# Dictionary.ContainsKey() - 它是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13262106/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 07:58:46  来源:igfitidea点击:

Dictionary.ContainsKey() - How does it work?

c#.netreference-typeidictionary

提问by Richard

I've read the MSDN documentation on how Dictionary.ContainsKey()works, but I was wondering how it actually makes the equality comparison? Basically, I have a dictionary keyed to a reference type* and I want the ContainsKey()method to check a certain property of that reference type as its basis for determining if the key exists or not. For example, if I had a Dictionary(MyObject, int)and MyObjecthas a public property (of int) called "TypeID", could I get ContainsKey(MyObject myObject)to check to see if one of the keys has a TypeIDthat is equal to myObject? Could I just overload the ==operator?

我已经阅读了关于如何Dictionary.ContainsKey()工作的 MSDN 文档,但我想知道它实际上是如何进行相等比较的?基本上,我有一个以引用类型为键的字典*,我希望该ContainsKey()方法检查该引用类型的某个属性,作为确定该键是否存在的基础。例如,如果我有 aDictionary(MyObject, int)并且MyObject有一个int名为“TypeID”的公共属性(of ),我是否可以ContainsKey(MyObject myObject)检查其中一个键的 aTypeID是否等于myObject?我可以让==操作符过载吗?

  • The reference type is an object called "Duration" which holds a value (double Length); "Duration" is a base type used in my music program to denote how long a particular sound lasts. I derive classes from it which incorporate more sophisticated timing concepts, like Western musical time signatures, but want all of them to be comparable in terms of their length.
  • 引用类型是一个名为“Duration”的对象,它包含一个值 ( double Length);“持续时间”是我的音乐程序中使用的基本类型,用于表示特定声音持续的时间。我从中派生出包含更复杂的时间概念的类,如西方音乐拍号,但希望它们在长度方面具有可比性。

EDIT: As suggested, I implemented IEquitable on my object like so:

编辑:按照建议,我在我的对象上实现了 IEquitable,如下所示:

 public class Duration : IEquatable<Duration>
 {
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
{
        get
        {
            return _length;
        }
        set
        {
            _length = value;
        }
    }

// removed all the other code that as it was irrelevant

    public override bool Equals(object obj)
    {
        Duration otherDuration = (Duration)obj;
        if (otherDuration._length == _length)
        {
            return true;
        }
        else
        {
            return false
        }
    }

}

Is this all I need to do?

这就是我需要做的全部吗?

采纳答案by vossad01

EDIT: here is code for your updated example. Note: I find it a little odd that you expose the field as protected, and also have a virtual property that exposes the member. Under this scheme something could override Lengthresulting in equality that looks at _lenghtto not behave as expected.

编辑:这是您更新示例的代码。注意:我发现将字段公开为受保护的,并且还有一个公开成员的虚拟属性,这有点奇怪。在这个方案下,某些东西可能会覆盖Length导致看起来_lenght不像预期那样表现的平等。

public class Duration : IEquatable<Duration>
{
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
    {
        get { return _length; }
        set { _length = value; }
    }

    // removed all the other code that as it was irrelevant

    public bool Equals(Duration other)
    {
        // First two lines are just optimizations
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        return _length.Equals(other._length);
    }

    public override bool Equals(object obj)
    {
        // Again just optimization
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;

        // Actually check the type, should not throw exception from Equals override
        if (obj.GetType() != this.GetType()) return false;

        // Call the implementation from IEquatable
        return Equals((Duration) obj);
    }

    public override int GetHashCode()
    {
        // Constant because equals tests mutable member.
        // This will give poor hash performance, but will prevent bugs.
        return 0;
    }
}


See EqualityComparer.Defaultfor information on the default IEqualityComparerused by the Dictionary class.

有关Dictionary 类使用的默认值的信息,请参阅EqualityComparer.DefaultIEqualityComparer

If you do not want to generally override GetHashCodeand Equalson the class, or if you are unable to. There is an overload of the Dictionary constructorin which you can provide the specific IEqualityComparerto use.

如果您不想一般地覆盖GetHashCodeEquals在类上,或者您无法覆盖。有一个Dictionary 构造函数重载,您可以在其中提供特定IEqualityComparer的使用。

It is a simple interface to implement, but you do need to be careful that you respect the contract for GetHashCodeor you can end up with unexpected behavior.

这是一个易于实现的简单接口,但您确实需要注意遵守约定,GetHashCode否则最终可能会出现意外行为。

public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.TypeID == y.TypeID;
    }

    public int GetHashCode(MyObject obj)
    {
        return obj.TypeID; //Already an int
    }
}

to use it just go

使用它就去

new Dictionary<MyObject, int>(new MyObjectEqualityComparer());   

If you want to use the default IEqualityComparer you need to provide roughly the same methods on MyObjectEqualityComparer. You canavoid overriding object.Equals()if you implement IEquatable. However I would strongly discourage it because doing so can create some surprising behavior. You are better of overriding Equalsso that you have consistent behavior for all calls to Equals and have hashing that properly matches Equals. I have had to fix a bug in inherited code caused by a past developer only implementing IEquatable.

如果您想使用默认的 IEqualityComparer,您需要在 MyObjectEqualityComparer 上提供大致相同的方法。您避免压倒一切object.Equals(),如果你执行IEquatable。但是,我强烈建议不要这样做,因为这样做会产生一些令人惊讶的行为。您最好进行覆盖,Equals以便您对 Equals 的所有调用具有一致的行为,并具有与 Equals 正确匹配的散列。我不得不修复由过去的开发人员仅实现的继承代码中的错误IEquatable.

回答by Kirill Polishchuk

Internally Dictionaryuses EqualityComparer. Firstly it will check whether key implements IEquatable. If key doesn't implement this interface, it will call Equalsmethod.

内部Dictionary使用EqualityComparer. 首先它会检查 key 是否实现IEquatable。如果key没有实现这个接口,就会调用Equals方法。