2013-08-06 2 views
1

У меня есть два объекта, которые что-то вроде этого:Равные, но не взаимозаменяемы

class Container { 
    public HashSet<Item> Items { get; } 
} 

class Item { 
    public Container Parent { get; set; } 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 
} 

Каждый Item экземпляр должен принадлежать к Container, например, и есть один-ко-многим между ними, что всегда синхронизированы с обоих концов.

Теперь я сталкиваюсь с реализацией метода, который сравнивает два экземпляра Item, чтобы узнать, соответствуют ли их значения Value1 и Value2. Этот метод будет не учитывать значение Parent, так как каждая пара экземпляров, которые я сравниваю, определенно отличается от этого значения, поэтому сделать этот метод будет бесполезным, так как тогда он будет иметь тот же результат (false), что и object.ReferenceEquals метод.

Мой вопрос заключается в следующем. Должен ли я реализовать этот метод как метод объекта public override bool Equals(object obj) (вместе с GetHashCode)? Или не исключает того, что он игнорирует его свойство Parent? Почему или почему нет? Альтернативная идея, которую я имею, - это просто реализовать ее как public bool EqualsIgnoreParent(Item other), которая не отменяет ничего; Тогда я мог бы позвонить из пользовательского компаратора.

+2

Это может быть беспорядочным, когда у вас есть значение равенства на изменяемом объекте, который вы помещаете в «HashSet», потому что для правильной работы хеш-код не должен меняться после его добавления в набор. –

+0

Я предпочитаю использовать 'IEqualityComparer' для изменяемых объектов вместо переопределения' Equals'. – CodesInChaos

+0

@CodesInChaos Да, это то, что я сделал. См. Мой ответ ниже. – HappyNomad

ответ

1

Благодаря mikez's comment и Эрика Липперта blog post, я узнал, что я не должен переопределить Item «s Equals или GetHashCode методов, поскольку Item» s случаев изменчивы и добавляют к HashSet.

Следуя предложению here, я решил отделить две концепции равенства.

  1. Идентификация. Я сделал не переопределить ItemEquals и GetHashCode методы. Я оставил эти методы, поскольку они находятся в object. Это то, что будет использовать HashSet.
  2. Эквивалентность. Я создал публичный одноэлементный класс под названием ValueComparer, который вложен в Item и реализует IEqualityComparer<Item>. Вот где я помещаю логику сравнения, которую я описываю в своем вопросе.
+0

Одна хорошая вещь о 'IEqualityComparer ' заключается в том, что 'HashSet ', 'Словарь ' и т. Д. Имеют перегрузки конструктора, которые позволяют вам их использовать. – CodesInChaos

+0

Я предпочитаю извлекать из 'EqualityComparer ' вместо того, чтобы внедрять 'IEqualityComparer ' непосредственно. – CodesInChaos

+0

@CodesInChaos Какое преимущество вы найдете в 'EqualityComparer '? Возможно, если вы прочитаете [здесь] (http://stackoverflow.com/q/5707347/122781) и [здесь] (http://stackoverflow.com/a/340526/122781), тогда вы передумаете. – HappyNomad

0

Я думаю, вы хотели реализовать IEquatable. поэтому я предлагаю вам просто перегружать Euqals (object other) с Euqals (Item other), в то время как перезаписывает Equals (object other), вызывая Equals (Item other).

конечно, сделать GetHashCode вернуть -1 всегда, чтобы заставить сравнение равенства быть сделано с помощью Equals.here это объяснить, почему нужно перезаписать GetHashCode метод - Why is it important to override GetHashCode when Equals method is overridden?

0

Да, вы можете реализовать это с Равных и GetHashCode, до тех пор, пока Value1 и Value2 не изменяются, или ваш хэш не зависит от изменений. (В противном случае Collections, используя ваш Hashcode не будет работать)

Кроме того, вы можете уменьшить класс Item для основных членов

class Item { 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 
} 

и поддерживать связь в дополнительном словаре: (который был бы эффективным, если вы реализуете GetHashCode правильно)

Dictionary<Item, Container> ContainersOfItems; 
+0

Что касается вашей второй идеи, я не вижу замены свойства «Родитель» статическим словарем в качестве улучшения. Я думаю, наоборот; это усложнит код без практической пользы. – HappyNomad

+0

Поддержание списков по обоим сторонам отношений уже усложняет код из-за избыточности;) Преимущество в том, что поле –

+0

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

-1

не делайте этого, если вы не 100% уверены, что член Родителя никогда не делает никакой разницы. Там может быть много случаев, о которых вы не думаете, (что, если вам нужно положить предметы из 2 контейнеров в одном словаре?), Поэтому я рекомендую придерживаться дизайна. Если по определению равенство определяется только другими членами, идите на это. В противном случае нет.
Вы всегда можете написать логику равенства конкретных случаев. Помните, что Dictionary, List, HashSet и другие коллекции позволяют определить пользовательский номер IEqualityComparer.
Есть еще один вариант, но будьте очень осторожны. Переопределите оба метода, но сделайте GetHashCode более толерантным, чем Equals, это означает, что неравные объекты имеют одинаковый хеш-код, а НЕ наоборот. Make GetHashCode игнорировать родителя, но не Equals. это ничего не повредит, а затем, если вы хотите игнорировать член Родитель в словарях, напишите IEqualityComparer, который сравнивает их так же, как GetHashCode.

+1

'Опция заключается в переопределении только GetHashCode'. Вы всегда должны либо переопределять как equals, либо gethashcode, либо ни один, ни один. Важно, чтобы оба они имели одинаковое семантическое значение. – Servy

+0

Как я увидел реализации словаря, 'GetHashCode' используется для вычисления начальной точки поиска в словаре. С этого момента dict ищет элемент, вызывая 'Equals'. Его НЕ НЕОБХОДИМО для хеш-кода быть уникальным, так как окончательная проверка равенства выполняется только «Равно». – RoadBump

+0

Конечно, нет, но важно, чтобы как «Равные», так и «GetHashCode» основывались на одной и той же концепции равенства. Если два объекта равны, но имеют другой хеш-код, потому что метод «Equals» использует равенство значений, а «GetHashCode» основан на эталонном равенстве (или наоборот), словари разбиваются; внезапно становится возможным добавить несколько одинаковых ключей, или он скажет, что элемент не существует, когда он делает это и т. д. – Servy

0

Я бы предположил, что это может быть более значимым для разделенного Пункта так это выглядит примерно так:

class Container<TContent> { 
    public HashSet<Item<TContent>> Items { get; } 
} 

class Item<TContent> { 
    public Container Parent; 
    public TContent Content; 
} 

Если вы сделаете это, то вы можете определить тип для хранения вашего контента, который имеет Equals метод подходит для этого типа и инициализируйте свой HashSet с помощью IEqualityComparer<Item<TConcent>>, который устанавливает равенство для каждого поля Item<TContent>Content.

+0

Я только что закончил реализацию решения. Модификация объектной модели была бы тяжелой и не нужна. – HappyNomad

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