2009-09-10 5 views
12

Я использовал словарь (TKey, TValue) для многих целей. Но я не сталкивался с каким-либо сценарием для реализации GetHashCode(), который, по моему мнению, объясняется тем, что мои ключи были из первичных типов, таких как int и string. Мне любопытно узнать сценарии (примеры реального мира), когда нужно использовать пользовательский объект для ключа и, таким образом, реализовать методы GetHashCode() Equals() и т. Д.Когда мы делаем GetHashCode() для Словаря?

И, если использование пользовательского объекта для ключа требует реализации этих функции?

+0

Возможный дубликат [Почему важно переопределить GetHashCode, когда метод Equals переопределен?] (Http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when- equals-method-is-overridden) – nawfal

ответ

13

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

Например, если ваш ключ так же просто, как

class Point { 
    public int X { get; set; } 
    public int Y { get; set; } 
} 

и вы хотите два Point s два считаются равными, если их X s равны и их Y s равны, то вам нужно будет переопределить Equals и GetHashCode.

+0

в отношении словаря , что должно быть равным и GetHashCode, если 1) Я редактирую объект на месте и заменяю 'point.X' на' point.Y'? Нужно ли удалить значение и добавить его в словарь? – LamonteCristo

2

Одним из примеров является создание сложного ключа (который является ключом, состоящим из более одного фрагмента данных). Этот составной ключ был бы настраиваемым типом, который должен был бы переопределить эти методы.

Например, предположим, что у вас есть кэш памяти в памяти, и вы хотите проверить, был ли адрес в кеше, чтобы сохранить дорогостоящую поездку в базу данных для ее получения. Давайте также скажем, что адреса уникальны с точки зрения их ул. 1 и почтовый индекс полей. Вы бы реализовать кэш с чем-то вроде этого:

class AddressCacheKey 
{ 
    public String StreetOne { get; set; } 
    public String ZipCode { get; set; } 

    // overrides for Equals and GetHashCode 
} 

и

static Dictionary<AddressCacheKey,Address> cache; 

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

1

У вас есть два вопроса.

  1. Когда вам нужно реализовать GetHashCode()
  2. ли вы когда-либо использовать объект для словаря ключ.

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

Для 2) вы должны использовать объект в любое время, когда у вас есть необходимость иметь постоянный поиск по времени от объекта к другому типу. Поскольку GetHashCode() возвращает числовое значение и ссылки на хранилища коллекций, нет штрафа за использование объекта над Int или строкой (помните, что строка является объектом).

9

Просто чтобы понять: Существует одна важная вещь, о Dictionary<TKey, TValue> и GetHashCode(): Словарь использует GetHashCode, чтобы определить, если два ключа равны, т.е. если <TKey> имеет пользовательского типа, вы должны заботиться о тщательно реализации GetHashCode(). Как заметил Эндрю Харе, это легко, если у вас есть простой тип, который однозначно идентифицирует ваш пользовательский объект. Если у вас есть комбинированный идентификатор, он становится немного сложнее.

В качестве примера рассмотрим комплексное число как TKey. Комплексное число определяется его реальным и его мнимой частью. Оба имеют простой тип, например. double. Но как бы вы определили, равны ли два комплексных числа? Вы реализуете GetHashode() для своего настраиваемого сложного типа и объединяете обе идентифицирующие части.

Дальнейшее чтение по последнему here.

UPDATE

Основываясь на комментарий Ergwun, я проверил поведение Dictionary<TKey, TValue>.Add с особым уважением к TKey «s реализации Equals(object) и GetHashCode(). I должен признаться, что я был очень удивлен результатами.

Указанные два объекта k1 и k2 типа TKey, две произвольные объекты v1 и v2 типа TValue и пустой словарь d типа Dictionary<TKey, TValue>, это то, что происходит при добавлении v1 с ключом k1 к d первой и v2 с ключом k2 второй (в зависимости от реализации TKey.Equals(object) и TKey.GetHashCode()):

k1.Equals(k2) k1.GetHashCode() == k2.GetHashCode() d.Add(k2, v2) 
false   false         ok 
false   true         ok 
true   false         ok 
true   true         System.ArgumentException 

Con clusion: Я был не прав, поскольку я изначально думал, что второй случай (где Equals возвращает false, но оба ключевых объекта имеют один и тот же хеш-код) поднимут ArgumentException. Но поскольку третий случай показывает словарь каким-то образом, он использует GetHashCode(). Во всяком случае, кажется хорошим советом, что два объекта одного типа и равны должны возвращать один и тот же хэш-код, чтобы убедиться, что экземпляры Dictionary<TKey, TValue> работают правильно.

+9

-1 Словарь НЕ использует 'GetHashCode()', чтобы определить, равны ли два ключа. То есть словарь может содержать отдельные записи, ключи которых имеют один и тот же хэш-код. Словарь может быть менее эффективным, но он все равно будет работать. – Ergwun

+2

+1 Для обновления :) – Ergwun

+0

Да, вы должны убедиться, что одинаковые объекты возвращают один и тот же хэш-код (см. Http://msdn.microsoft.com/en-us/library/ms182358.aspx). – Ergwun

Смежные вопросы