Перегрузка оператора c помощью интерфейсного программирования в C#
фон
Я использую программирование на основе интерфейса в текущем проекте и столкнулся с проблемой при перегрузке операторов (в частности, операторов равенства и неравенства).
предположения
- я использую C# 3.0, .NET 3.5 и Visual Studio 2008
обновление - следующее предположение было ложным!
- требуется, чтобы все сравнения использовали Equals, а не operator== не является жизнеспособное решение, особенно при передаче типов в библиотеки (например, коллекции).
причина, по которой я был обеспокоен требованием использовать Equals, а не operator==, заключается в том, что я не мог найти нигде в руководящих принципах .NET, которые он заявил, что он будет использовать Equals, а не operator== или даже предложить его. Однако, после повторного прочтения руководящие принципы для того, чтобы переопределить Equals и оператор== я нашел вот это:
по умолчанию, оператор = = проверяет равенство ссылок, определяя, указывают ли две ссылки на один и тот же объект. Поэтому ссылочные типы не должны реализовывать operator ==, чтобы получить эту функциональность. Когда тип является неизменяемым, то есть данные, содержащиеся в экземпляре, не могут быть изменены, перегрузка operator == для сравнения равенства значений вместо равенства ссылок может быть полезна, поскольку в качестве неизменяемых объектов они могут считаться одинаковыми, если они имеют одинаковое значение. Это не хорошая идея переопределить оператор == в не неизменяемых типах.
интерфейс IEquatable используется универсальными объектами коллекции, такими как Словарь, Список и LinkedList при тестировании на равенство в таких методах, как Contains, IndexOf, LastIndexOf и Remove. Он должен быть реализован для любого объекта, который может храниться в родовой коллекция.
ограничения
- любое решение не должно требовать приведения объектов от их интерфейсов к их конкретным типам.
2 ответов:
короткий ответ: я думаю, что ваше второе предположение может быть ошибочным.
Equals()Это правильный способ проверить семантического равенства из двух объектов, неoperator ==.
длинный ответ: разрешение перегрузки операторов выполняется во время компиляции, а не выполнения.
если компилятор не может точно знать типы объектов, к которым он применяет оператор, он не будет компилироваться. Так как компилятор не может быть уверен, что это
IAddressбудет что-то, что имеет переопределение для==определено, он возвращается к значению по умолчаниюoperator ==реализацияSystem.Object.чтобы увидеть это более четко, попробуйте определить
operator +наAddressи добавить дваIAddressэкземпляров. если вы явно не приведете кAddress, это не будет компилироваться. Зачем? Потому что компилятор не может сказать, что конкретныйIAddressэтоAddress, и нет никакого значения по умолчаниюoperator +реализация упасть обратно вSystem.Object.
часть вашего разочарования, вероятно, связана с тем, что
Objectосуществляетoperator ==, и всеObject, поэтому компилятор может успешно разрешать такие операции, какa == bдля всех типов. Когда вы переехали==, вы ожидали увидеть такое же поведение, но не сделали, и это потому, что лучшее соответствие компилятор может найти оригиналObjectреализация.требуется, чтобы все сравнения использовали равные rather than operator== не является жизнеспособным решением, особенно при передаче ваших типов в библиотеки (например, коллекции).
на мой взгляд, это именно то, что вы должны делать.
Equals()Это правильный способ проверить семантического равенства двух объектов. иногда семантическое равенство-это просто ссылочное равенство, и в этом случае вам не нужно ничего менять. В других случаях, как в вашем примере, вы будете переопределятьEqualsкогда вам нужно сильнее контракта равенство, как равенство ссылок. Например, вы можете рассмотреть дваPersonsравны, если они имеют один и тот же номер социального страхования, или дваVehiclesравны, если у них одинаковый VIN.но
Equals()иoperator ==не одно и то же. Всякий раз, когда вам нужно переопределитьoperator ==, вы должны переопределитьEquals(), но почти никогда наоборот.operator ==это скорее синтаксическое удобство. Некоторые языки CLR (например, Visual Basic.NET) даже не разрешаю вам переопределите оператор равенства.
мы столкнулись с той же проблемой и нашли отличное решение: пользовательские шаблоны Resharper.
мы настроили всех наших пользователей на использование общего глобального каталога шаблонов в дополнение к их собственному и поместили его в SVN, чтобы он мог быть версионным и обновленным для всех.
каталог включал все шаблоны, известные как неправильные в нашей системе:
$i1$ == $i2$(где i1 и i2 -выражения нашего типа интерфейса, или полученный.заменить шаблон
$i1$.Equals($i2$)и серьезность "показать как ошибка".
точно так же у нас
$i1$ != $i2$надеюсь, что это помогает. П. С. глобальные каталоги-это объект для ReSharper 6.1 (ВП), очень скоро будет помечен как окончательный.
обновление: я подал Resharper Issue чтобы отметить весь интерфейс '= = ' предупреждение, если оно не сравнивается с null. Пожалуйста, голосуйте, если вы считаете, что это достойно особенность.
обновление 2: Resharper также имеет атрибут [CannotApplyEqualityOperator], который может помочь.
Comments