Почему мы должны определять оба == и! = В C #? - PullRequest
339 голосов
/ 02 августа 2011

Компилятор C # требует, чтобы всякий раз, когда пользовательский тип определял оператор ==, он также определял != (см. здесь ).

Почему?

Мне любопытно узнать, почему дизайнеры посчитали это необходимым и почему компилятор не может по умолчанию использовать разумную реализацию для одного из операторов, когда присутствует только другой.Например, Lua позволяет вам определять только оператор равенства, а другой вы получаете бесплатно.C # мог бы сделать то же самое, попросив вас определить либо ==, либо оба == и! =, А затем автоматически скомпилировать отсутствующий оператор! = Как !(left == right).

Я понимаю, что существуют странные угловые случаи, когда некоторыесущности не могут быть ни равными, ни неравными (как, например, NaN IEEE-754), но они кажутся исключением, а не правилом.Так что это не объясняет, почему разработчики компилятора C # сделали исключение из правила.

Я видел случаи плохого качества изготовления, когда оператор равенства определен, тогда оператор неравенства является копией-вставкой с каждым икаждое сравнение отменяется, и каждый && переключается на ||(Вы понимаете, в чем дело ... в основном! (a == b) расширено по правилам Де Моргана).Это плохая практика, которую компилятор может исключить при разработке, как в случае с Lua.

Примечание: То же самое верно для операторов <> <=> =.Я не могу представить случаи, когда вам нужно будет определить их неестественным образом.Lua позволяет вам определять только <и <= и определяет> = и> естественно через отрицание прежних.Почему C # не делает то же самое (по крайней мере, «по умолчанию»)?

РЕДАКТИРОВАТЬ

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

Суть моего вопроса, однако, заключается в том, почему это принудительно требуется в C #, когда обычно это не логически необходимо?

Это также поразительно отличается от выбора дизайна для .NET-интерфейсов, таких как Object.Equals, IEquatable.Equals IEqualityComparer.Equals, где отсутствие аналога NotEquals показывает, что платформа учитывает !Equals() объекты как неравные и это все.Кроме того, классы, подобные Dictionary, и методы, подобные .Contains(), зависят исключительно от вышеупомянутых интерфейсов и не используют операторы напрямую, даже если они определены.Фактически, когда ReSharper генерирует элементы равенства, он определяет и ==, и != в терминах Equals(), и даже тогда, только если пользователь вообще решает генерировать операторы.Операторы равенства не нужны платформе для понимания равенства объектов.

По сути, .NET Framework не заботится об этих операторах, он только заботится о нескольких Equals методах.Решение требовать, чтобы операторы == и! = Были определены пользователем в тандеме, связано исключительно с языковым дизайном, а не с семантикой объектов в отношении .NET.

Ответы [ 13 ]

2 голосов
/ 03 августа 2011

Короче говоря, принудительная последовательность.

'==' и '! =' Всегда являются истинными противоположностями, независимо от того, как вы их определяете, как таковые определяются их словесным определением «равно» и «не равно». Определяя только одно из них, вы открываете себя для несогласованности операторов равенства, когда оба «==» и «! =» Могут быть оба истинными или оба ложными для двух заданных значений. Вы должны определить оба, так как, когда вы решите определить одно, вы должны также определить другое соответствующим образом, чтобы было совершенно ясно, каково ваше определение «равенства». Другое решение для компилятора состоит в том, чтобы позволить вам только переопределить '==' OR '! =' И оставить другое как неотъемлемое отрицание другого. Очевидно, что это не относится к компилятору C #, и я уверен, что есть веская причина для этого, которая может быть строго объяснена выбором простоты.

Вопрос, который вы должны задать: «зачем мне переопределять операторы?» Это сильное решение, которое требует серьезных аргументов. Для объектов '==' и '! =' Сравните по ссылке. Если вы хотите переопределить их, чтобы НЕ сравнивать по ссылке, вы создаете общую несогласованность операторов, которая не очевидна для любого другого разработчика, который будет просматривать этот код. Если вы пытаетесь задать вопрос «является ли состояние этих двух экземпляров эквивалентным?», То вам следует реализовать IEquatible, определить Equals () и использовать этот вызов метода.

Наконец, IEquatable () не определяет NotEquals () по той же причине: потенциальная возможность раскрыть несоответствия операторов равенства. NotEquals () должен ВСЕГДА возвращать! Equals (). Открыв определение NotEquals () для класса, реализующего Equals (), вы вновь ставите вопрос о последовательности в определении равенства.

Редактировать: Это просто мои рассуждения.

2 голосов
/ 02 августа 2011

Языки программирования представляют собой синтаксические перестановки исключительно сложных логических выражений.Имея это в виду, можете ли вы определить случай равенства без определения случая неравенства?Ответ - нет.Чтобы объект a был равен объекту b, обратное значение объекта a, отличного от b, также должно быть истинным.Еще один способ показать это:

if a == b then !(a != b)

. Это обеспечивает определенную способность языка определять равенство объектов.Например, сравнение NULL! = NULL может бросить рывок в определение системы равенства, которая не реализует оператор неравенства.

Теперь, что касается идеи! = Просто заменяемого определения, как в

if !(a==b) then a!=b

Я не могу с этим поспорить.Однако, скорее всего, группа спецификаций языка C # решила, что программисту придется явно определять равенство и неравенство объекта вместе

0 голосов
/ 02 августа 2011

Возможно, что-то, о чем они не думали, не успело сделать.

Я всегда использую ваш метод, когда перегружаю ==. Тогда я просто использую это в другом.

Вы правы, с небольшим объемом работы, компилятор может дать нам это бесплатно.

...