开发者

Dictionary.ContainsKey return False, but a want True

namespace Dic
{
public class Key
{
    string name;
    public Key(string n) { name = n; 开发者_Go百科}
}

class Program
{
    static string Test()
    {
        Key a = new Key("A");
        Key b = new Key("A");
        System.Collections.Generic.Dictionary<Key, int> d = new System.Collections.Generic.Dictionary<Key, int>();
        d.Add(a, 1);
        return d.ContainsKey(b).ToString();
    }

    static void Main(string[] args)
    {
        System.Console.WriteLine(Test());
    }
}
}

What should I change to get true?


You want true - but a and b are different objects.

You need to override GetHashCode and Equals on class Key

public class Key
{
    string name;
    public Key(string n) { name = n; }

    public override int GetHashCode()
    {
        if (name == null) return 0;
        return name.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        Key other = obj as key;
        return other != null && other.name == this.name;
    }
}


It would probably help if you override Key.GetHashCode and Key.Equals.

In Key:

public override bool Equals(object obj)
{
    var k = obj as Key;
    if (k != null)
    {
        return this.name == k.name;
    }
    return base.Equals(obj);
}

public override int GetHashCode()
{
    return this.name.GetHashCode();
}


If you do not have the ability to override equality operators/Equals/GetHashCode as others have mentioned (as in, you do not control the source code of the object), you can provide an IEqualityComparer<Key> implementation in the constructor of the dictionary to perform your equality checks.

class KeyComparer : IEqualityComparer<Key>
{
    public bool Equals(Key x, Key y)
    {
        return x.Name == y.Name;
    }

    public int GetHashCode(Key obj)
    {
        return obj.Name.GetHashCode();
    }
}

As it stands, your Key is a reference object, so equality is only determined on reference unless you tell the world (or the dictionary) otherwise.


Overriding a class' GetHashCode and Equals methods so that it will work properly in a dictionary is not a very good approach. The way a dictionary behaves should be an implementation detail of the dictionary, not of whatever class is being used as a key. You'll get into trouble when you want to use the class in different dictionaries with different behavior. Or if you don't have access to the class source code.

A better mouse trap is to give the dictionary its own comparer. For example:

using System;
using System.Collections.Generic;

class Program {
    static void Main(string[] args) {
        var d = new Dictionary<Key, int>(new MyComparer());
        d.Add(new Key("A"), 1);
        Console.WriteLine(d.ContainsKey(new Key("a")));
        Console.ReadLine();
    }
    private class MyComparer : IEqualityComparer<Key> {
        public bool Equals(Key x, Key y) {
            return string.Compare(x.Name, y.Name, true) == 0;
        }
        public int GetHashCode(Key obj) {
            return obj.Name.ToUpper().GetHashCode();
        }
    }
    public class Key {
        public string Name { get; set; }
        public Key(string name) { Name = name; }
    }
}


In order to use your own classes as dictionary keys, you should override GetHashCode and Equals. Otherwise it will use the memory address to check for equality.

    public class Key
    {
        string name;
        public Key(string n) { name = n; }

        public override int GetHashCode()
        {
            return name.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            var other = obj as Key;
            if( other == null )
                return false;

            return name == other.name;
        }
    }


you need to override Equals and GetHashCode methods of your Key class.


you problem is that

new Key("A").Equals(new Key("A"))==false.

and

new Key("A").GetHashCode()!=new Key("A").GetHashCode()

fix that and it should work I think. To fix it override the Equals method and check if the name values are the same. You should also override GetHashCode if you are overriding Equals.


1. Override Equals, Get Hash Code, and the '==' Operator.

The Key class must override Equals in order for the Dictionary to detect if they are the same. The default implementation is going to check references only.

Here:

        public bool Equals(Key other)
        {
            return this == other;
        }

        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Key))
            {
                return false;
            }

            return this.Equals((Key)obj);
        }

        public static bool operator ==(Key k1, Key k2)
        {
            if (object.ReferenceEquals(k1, k2))
            {
                return true;
            }

            if ((object)k1 == null || (object)k2 == null)
            {
                return false;
            }

            return k1.name == k2.name;
        }

        public static bool operator !=(Key k1, Key k2)
        {
            if (object.ReferenceEquals(k1, k2))
            {
                return false;
            }

            if ((object)k1 == null || (object)k2 == null)
            {
                return true;
            }

            return k1.name != k2.name;
        }

        public override int GetHashCode()
        {
            return this.name == null ? 0 : this.name.GetHashCode();
        }

2. If Possible, use a struct.

You should use a struct for immutable datatypes like this, since they are passed by value. This would mean that you coulnd't accidentally munge two different values into the same key.


they have the same values internally but a != b as they are 2 different variables.


You need to override the Equals and GetHashCode methods of your Key class. In your case you could compare based on the key's name (or on any other unique property if your class is more complex).

public class Key {
    string name;
    public Key(string n) { name = n; }

    public override bool Equals(object obj) {
        Key k = obj as Key;
        if (k == null)
            return false;
        return name.Equals(k.name);
    }

    public override int GetHashCode() {
        return name.GetHashCode();
    }
}


Then you will need to override GetHashCode and Equals on the Key class.

Without doing that, you get the default implementation of both. Which results in the hashcode for a and b to be most likely not the same (I don't know how what the default implementation looks like), and a to be definitely not equal to b (the default Equals() implementation checks reference equality).

In your case, assuming "name" is not a null, it could be implemented as

   public class Key
   {
        string name;
        public override int GetHashCode()
        {
             return name.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
              return false;
            }

            Key objAsKey = obj as Key;
            if (objAsKey == null)
            {
              return false;
            }

            return this.name.Equals(objAsKey.Name);
        }
    }

Whether this is a satisfactory hash is a different story, but it shows the principle, nevertheless.


ContainsKey in this case is comparing Key as objects and checking to see if the objects themselves are the same -- they are not. You need to implement IComparable or override Key.Equals or something along those lines to get it to do what you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜