IDictionary: Использование пользовательского класса в качестве ключа - PullRequest
0 голосов
/ 20 июля 2011

Я использовал IDictionary<KeyClass, ValueClass> в одном из моих проектов. KeyClass очень просто, содержит всего два целых числа.

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}
}

Я обращался к значениям из словаря следующим образом:

var k = new KeyClass(1, 1);
var v = myDictionary[k];

В словаре наверняка есть ключ KeyClass с ValueA = 1 и ValueB = 1, а также множество других ключей. Но я получил исключение:

The given key is not present in the dictionary.

Я реализовал IComparable в классе KeyClass, но это не решило мою проблему. Я гуглил и нашел эту CodeProject статью. Он описывает следующую технику для использования класса, реализующего интерфейс IEqualityComparer<T>. Я определил это в моем классе:

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public class EqualityComparer : IEqualityComparer<KeyClass>
    {
        public bool Equals(KeyClass a, KeyClass b)
        {
            return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
        }

        public int GetHashCode(KeyClass k)
        {
            return k.ValueA ^ k.ValueB;
        }
    }
}

Теперь я должен объявить IDictionary<KeyClass, ValueClass> следующим образом:

var d = new Dictionary<KeyClass, ValueClass>(new KeyClass.EqualityComparer());

После этого все работало нормально.

У меня вопрос, что я использовал тот же класс в среде сохранения (.Net 4.0, настольное приложение Windows 7) и KeyClass работал без реализации IEqualityComparet<T> в другом проекте, но почему он перестал работать после того, как я поместил классы в этом проекте?

Ответы [ 2 ]

1 голос
/ 22 апреля 2012

Нет оснований для реализации IEqualityComparer<KeyClass>.Вместо этого переопределите методы GetHashCode и Equals в KeyClass.

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        KeyClass p = obj as KeyClass;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
    }

    public bool Equals(KeyClass p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return this.ValueA == p.ValueA && this.ValueB == b.ValueB;
    }

    public int GetHashCode(KeyClass k)
    {
        return k.ValueA ^ k.ValueB;
    }
}

. Вам нужно только IEqualityComparer<KeyClass>, если вы делаете пользовательское сравнение - что-то, что отличается от сравнения по умолчанию.Но так как вы управляете KeyClass, вы можете указать сравнение по умолчанию в классе.

Если вы сделаете это таким образом, вам не нужно будет предоставлять компаратор при создании словаря.То есть вы можете написать:

var d = new Dictionary<KeyClass, ValueClass>();

См. Руководство по Equals () и Оператору == .

Причина, по которой это раньше не работало, возможно, состоит в том, чтоСравнением по умолчанию для ссылочных типов является равенство ссылок.Поэтому, если вы напишите:

var k = new KeyClass(1, 1);
var v = new ValueClass(..);
d.Add(k, v);

var k1 = new KeyClass(1, 1);
var v1 = d[k1];

Вы получите «ключ не найден», потому что «k» и «k1» - это не один и тот же объект.Но если вы написали:

var v1 = d[k];

Вы найдете значение, потому что k - это ключ, который вы сохранили в словаре.

Переопределение Equals решает эту проблему.

0 голосов
/ 22 апреля 2012

Класс является ссылочным типом - по умолчанию одна ссылка сравнивается с другой на основе того, указывают ли они на один и тот же экземпляр класса, а не на значения его свойств.

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

Если вы хотите такое поведение, вы должны либо использоватьтип значения (структура вместо класса), пользовательский компаратор (как вы это сделали) или вы должны переопределить методы Equals () и GetHashCode () KeyClass.

Использование структуры, вероятно, является лучшимвариант в этом случае, но важно понимать последствия их использования, о которых вы можете прочитать здесь: Struct (Руководство по программированию в C #)

...