Dictionary.ContainsKey возвращает False, но хочет True - PullRequest
16 голосов
/ 10 июня 2010
namespace Dic
{
public class Key
{
    string name;
    public Key(string n) { name = n; }
}

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());
    }
}
}

Что я должен изменить, чтобы стать правдой?

Ответы [ 12 ]

35 голосов
/ 10 июня 2010

Вы хотите true, но a и b - разные объекты.

Вам необходимо переопределить GetHashCode и Equals для класса 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;
    }
}
8 голосов
/ 10 июня 2010

Вероятно, это поможет при переопределении Key.GetHashCode и Key.Equals.

В 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();
}
6 голосов
/ 10 июня 2010

Если у вас нет возможности переопределить операторы равенства / Equals / GetHashCode, как уже упоминали другие (например, вы не управляете исходным кодом объекта), вы можете предоставить реализацию IEqualityComparer<Key> в конструкторе словарь для проверки вашего равенства.

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();
    }
}

В своем нынешнем виде ваш Ключ является эталонным объектом, поэтому равенство определяется только по ссылке, если вы не скажете миру (или словарю) иначе.

4 голосов
/ 10 июня 2010

Переопределение методов класса GetHashCode и Equals для правильной работы в словаре - не очень хороший подход.Поведение словаря должно быть деталью реализации словаря, а не того, какой класс используется в качестве ключа.У вас будут проблемы, когда вы захотите использовать класс в разных словарях с разным поведением.Или, если у вас нет доступа к исходному коду класса.

Лучшая ловушка мыши - дать словарю свой собственный компаратор.Например:

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; }
    }
}
2 голосов
/ 10 июня 2010

Чтобы использовать ваши собственные классы в качестве ключей словаря, вы должны переопределить GetHashCode и Equals. В противном случае он будет использовать адрес памяти для проверки на равенство.

    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;
        }
    }

1 голос
/ 10 июня 2010

вам нужно переопределить методы Equals и GetHashCode вашего класса Key.

1 голос
/ 10 июня 2010

Вы проблема в том, что

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

и

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

исправить это, и это должно работать, я думаю. Чтобы исправить это, переопределите метод Equals и проверьте, совпадают ли значения имени. Вы также должны переопределить GetHashCode, если вы переопределяете Equals.

0 голосов
/ 10 июня 2010

Тогда вам нужно переопределить GetHashCode и Equals для класса Key.

Без этого вы получаете реализацию обоих по умолчанию. Что приводит к тому, что хэш-код для a и b, скорее всего, не совпадает (я не знаю, как выглядит реализация по умолчанию), а a точно не равен b (реализация Equals () по умолчанию проверяет равенство ссылок ).

В вашем случае, предполагая, что «имя» не является нулем, оно может быть реализовано как

   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);
        }
    }

Является ли это удовлетворительным хешем, это отдельная история, но, тем не менее, он показывает принцип.

0 голосов
/ 10 июня 2010

1.Переопределить Equals, Get Hash Code и оператор '=='.

Класс Key должен переопределить Equals, чтобы Словарь мог определить, совпадают ли они.Реализация по умолчанию будет проверять только ссылки.

Здесь:

        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.Если возможно, используйте структуру.

Вы должны использовать структуру для таких неизменяемых типов данных, поскольку они передаются по значению.Это будет означать, что вы не можете случайно вставить два разных значения в один и тот же ключ.

0 голосов
/ 10 июня 2010

Вам необходимо переопределить методы Equals и GetHashCode вашего класса Key.В вашем случае вы можете сравнить на основе имени ключа (или любого другого уникального свойства, если ваш класс более сложный).

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();
    }
}
...