Объект как ключ в словаре - PullRequest
       28

Объект как ключ в словаре

1 голос
/ 17 января 2011

У меня есть этот класс:

class A {
    string name;
    string code;
}

Примеры:

A1: name="blabla", code="kuku"
A2: name="blabla", code=null
A3: name=null, code="kuku"

Dictionary<A, string> d=new Dictionary<A, string>();

d[A1]="aaa";
d[A2]="bbb"
results: d[A1]="bbb";

d[A1]="aaa";
d[A3]="bbb"
results: d[A1]="bbb";

d[A2]="aaa";
d[A3]="bbb"
results: d[A2]="aaa"; d[A3]="bbb";

Есть ли способ реализовать класс A в качестве ключа к словарю?

Ответы [ 3 ]

3 голосов
/ 17 января 2011

Вместо реализации GetHashCode лучшим вариантом будет реализация собственного IEqualityComparer<A> и передача его в конструктор для вашего словаря. При этом ваши правила эквивалентности не имеют смысла. Эквивалентность в программировании должна соответствовать стандартным алгебраическим правилам эквивалентности.

В вашем случае у вас есть три объекта, которые считаются "равными", что, кажется, основано на том, равно ли одно из двух свойств. Однако этот подход не предусматривает транзитивного равенства, которое необходимо. # 1 = # 2 из-за названия. # 1 = # 3 из-за кода. Однако равенство и эквивалентность требуют, чтобы, если a = b и b = c, то a = c. В вашем случае сравнение # 2 и # 3 не показывает эквивалентности или равенства, поскольку у них нет соответствующих свойств, даже если они оба равны # 1.

Короткая версия заключается в том, что ваши правила равенства / эквивалентности нельзя использовать ни с одним хранилищем на основе ключей, например Dictionary.

Если вы убеждены, что это правильный путь, тогда это сделает то, что вы ищете, , но это не будет вести себя последовательно. Ничто не может вести себя последовательно, используя эту псевдоэквивалентность .

class AEqualityComparer : IEqualityComparer<A>
{
    public bool Equals(A x, A y)
    {
        return x == y || x.name == y.name || x.code == y.code;
    }

    public int GetHashCode(A x)
    {
        return -1; // impossible to compute; will negatively impact performance
    }
}

...

Dictionary<A, string> dict = new Dictionary<A, string>(new AEqualityComparer());

Однако это очень легко сломать:

dict[A1] = "foo";
dict[A2] = "bar";

// dict[A1] is now "bar", as expected

dict[A3] = "baz";

Здесь dict[A1] - это "baz", как и ожидалось. Однако dict[A2] равно и "baz", даже если A2 не равно A3 в соответствии с этими правилами. Это потому, что исходный объект был A1, что равно обоим.

3 голосов
/ 17 января 2011

Вы не можете.

Equals / GetHashCode реализации должны формировать [отношение эквивалентности] - они должны удовлетворять следующим свойствам:

  • a == a. (Рефлексивность)
  • если a == b, то b == a. (Симметрия)
  • если a == b и b == c, то a == c. (Транзитивность)

Ваше определение не является переходным; (a, b) == (b, c) и (b, c) == (c, d), но (a, b)! = (c, d).

1 голос
/ 17 января 2011

Чтобы ответить на ваш отредактированный вопрос:

Создайте два разных строковых словаря (один для Имени и один для Кода) и найдите тот, для которого у вас есть значение.

...