Я не могу говорить за дизайнеров языка, но из того, что я могу рассуждать, кажется, что это было намеренное, правильное дизайнерское решение.
Глядя на этот базовый код F #, вы можете скомпилировать его в рабочую библиотеку. Это допустимый код для F #, и он перегружает только оператор равенства, а не неравенство:
module Module1
type Foo() =
let mutable myInternalValue = 0
member this.Prop
with get () = myInternalValue
and set (value) = myInternalValue <- value
static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
//static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop
Это именно так и выглядит. Он создает компаратор равенства только на ==
и проверяет, равны ли внутренние значения класса.
Хотя вы не можете создать такой класс в C #, вы можете использовать класс, скомпилированный для .NET. Очевидно, он будет использовать наш перегруженный оператор для ==
Итак, что же использует среда выполнения для !=
?
Стандарт C # EMCA содержит целый набор правил (раздел 14.9), объясняющих, как определить, какой оператор использовать при оценке равенства. Говоря слишком упрощенно и, следовательно, не вполне точно, если сравниваемые типы имеют одинаковый тип и , если присутствует перегруженный оператор равенства, он будет использовать эту перегрузку, а не стандартное ссылочное равенство оператор, унаследованный от Object. Поэтому неудивительно, что если присутствует только один из операторов, он будет использовать оператор равенства ссылок по умолчанию, который есть у всех объектов, перегрузка для него отсутствует. 1
Зная, что это так, реальный вопрос заключается в следующем: почему это было разработано таким образом, и почему компилятор не может понять это самостоятельно? Многие люди говорят, что это не было дизайнерским решением, но мне нравится думать, что оно было продумано таким образом, особенно учитывая тот факт, что все объекты имеют оператор равенства по умолчанию.
Итак, почему компилятор не создает автоматически оператор !=
? Я не могу знать наверняка, если кто-то из Microsoft не подтвердит это, но это то, что я могу определить, исходя из фактов.
Для предотвращения неожиданного поведения
Возможно, я хочу сделать сравнение значений на ==
, чтобы проверить равенство. Однако, когда дело дошло до !=
, мне было все равно, равны ли значения, если только ссылки не равны, потому что моя программа считает их равными, меня интересует только совпадение ссылок. В конце концов, это фактически обозначено как поведение C # по умолчанию (если оба оператора не были перегружены, как было бы в случае некоторых библиотек .net, написанных на другом языке). Если компилятор автоматически добавлял код, я больше не мог полагаться на то, что компилятор будет выводить код, который должен быть совместимым. Компилятор не должен писать скрытый код, который меняет ваше поведение, особенно если код, который вы написали, соответствует стандартам C # и CLI.
С точки зрения этого заставляет перегрузить его, вместо того, чтобы перейти к поведению по умолчанию, я могу только твердо сказать, что оно в стандарте (EMCA-334 17.9.2) 2 . Стандарт не уточняет почему. Я считаю, что это связано с тем, что C # заимствует много поведения из C ++. Подробнее об этом см. Ниже.
Когда вы переопределяете !=
и ==
, вам не нужно возвращать bool.
Это еще одна вероятная причина. В C # эта функция:
public static int operator ==(MyClass a, MyClass b) { return 0; }
действует так же, как этот:
public static bool operator ==(MyClass a, MyClass b) { return true; }
Если вы возвращаете что-то отличное от bool, компилятор не может автоматически вывести противоположный тип. Кроме того, в случае, когда ваш оператор возвращает bool, им просто не имеет смысла создавать генерируемый код, который будет существовать только в этом конкретном случае, или, как я уже говорил выше, код, который скрывает Поведение CLR по умолчанию.
C # многое заимствует из C ++ 3
Когда появился C #, в журнале MSDN была статья, в которой говорилось о C #:
Многие разработчики хотели бы, чтобы существовал язык, который было бы легко писать, читать и поддерживать, как Visual Basic, но который по-прежнему обеспечивал мощь и гибкость C ++.
Да, цель разработки для C # заключалась в том, чтобы дать почти такое же количество энергии, что и в C ++, и лишь немного пожертвовать такими удобствами, как жесткая безопасность типов и сборка мусора.C # был строго смоделирован после C ++.
Возможно, вы не удивитесь, узнав, что в C ++ операторы равенства не должны возвращать bool , как показано в в этом примере программы
Теперь C ++ напрямую не требует перегрузки дополнительного оператора.Если вы скомпилировали код в примере программы, вы увидите, что он работает без ошибок.Однако, если вы попытались добавить строку:
cout << (a != b);
, вы получите
ошибка компилятора C2678 (MSVC): бинарный '! =': Оператор не найден, который занимает левуюоперанд типа «Тест» (или нет приемлемого преобразования) `.
Итак, хотя сам C ++ не требует перегрузки в парах, он не будет позволяет использовать оператор равенства, который вы не перегружали в пользовательском классе.Это действительно в .NET, потому что все объекты имеют объект по умолчанию;C ++ не делает.
1.В качестве примечания, стандарт C # по-прежнему требует перегрузки пары операторов, если вы хотите перегрузить один из них.Это часть стандарта , а не просто компилятор .Однако при доступе к библиотеке .net, написанной на другом языке, который не соответствует этим требованиям, применяются те же правила, что и при определении того, какой оператор вызывать.
2. EMCA-334 (pdf) (http://www.ecma -international.org / публикации / файлы / ECMA-ST / Ecma-334.pdf )
3.И Java, но на самом деле не в этом дело