Dictionary.ContainsKey возвращает False, но хочет True

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

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

Ответы

Ответ 1

Вы хотите 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;
    }
}

Ответ 2

Вероятно, это поможет, если вы переопределите 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();
}

Ответ 3

Если у вас нет возможности переопределить операторы равенства /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

Переопределение методов класса 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; }
    }
}

Ответ 5

Чтобы использовать ваши собственные классы в качестве словарных ключей, вы должны переопределить 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;
        }
    }

Ответ 6

проблема заключается в том, что

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

и

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

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

Ответ 7

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

Ответ 8

они имеют одинаковые значения внутри, но a!= b, поскольку они являются двумя разными переменными.

Ответ 9

Вам необходимо переопределить методы 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();
    }
}

Ответ 10

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

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

Ответ 11

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

Без этого вы получаете стандартную реализацию обоих. Какие результаты в hashcode для 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);
        }
    }

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

Ответ 12

ContainsKey в этом случае сравнивает Key как объекты и проверяет, являются ли сами объекты одинаковыми - они не являются. Вам необходимо реализовать IComparable или переопределить Key.Equals или что-то в этом направлении, чтобы заставить его делать то, что вы хотите.