Почему C # и Java используют ссылочное равенство как значение по умолчанию для '=='?

Я размышлял какое-то время, почему Java и C # (и я уверен, что другие языки) по умолчанию ссылаются на равенство для ==.

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

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

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

Есть ли какое-то существенное преимущество в том, что я просто отсутствую, или кажется разумным, что поведение по умолчанию должно быть логическим равенством, а по умолчанию для возврата к эталонному равенству логическое равенство не существует для класса?

30 голосов | спросил Zipper 9 J0000006Europe/Moscow 2013, 08:24:43

2 ответа


28

C # делает это, потому что Java. Java, потому что Java не поддерживает перегрузку оператора. Поскольку для каждого класса должно быть переопределено равенство значений, оно не может быть оператором, но вместо этого должно быть методом. ИМО это было плохое решение. Гораздо проще писать и читать a == b, чем a.equals(b) и гораздо более естественным для программистов с опытом C или C ++, но a == b почти всегда ошибочен. Ошибки использования == где .equals потребовалось потратить бесчисленные тысячи часов программиста.

ответил kevin cline 9 J0000006Europe/Moscow 2013, 09:13:39
14

Короткий ответ: Консистенция

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

  • Ссылка равенство : означает, что а = Ь верно, если а и б относятся к одному объекту. Было бы неверно, если a и b относились к разным объектам, даже если все атрибуты a и b были одинаковыми.
  • Неверное равенство : означает, что a = b истинно, если все атрибуты объектов, к которым относятся a и b, идентичны. Неверное равенство может быть легко реализовано путем поразрядного сравнения пространства памяти, представляющего два объекта. Обратите внимание, что ссылочное равенство подразумевает неглубокое равенство
  • Глубокое равенство : означает, что a = b истинно, если каждый атрибут в a и b либо идентичен, либо глубоко равен. Обратите внимание, что глубокое равенство подразумевается как ссылочным равенством, так и неглубоким равенством. В этом смысле глубокое равенство является самой слабой формой равенства, а ссылочное равенство является самым сильным.

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

В нетривиальных системах равенство объектов часто определяется как нечто между глубоким и ссылочным равенством. Чтобы проверить, хотим ли мы рассматривать два объекта как равные в определенном контексте, нам может потребоваться сравнить некоторые атрибуты, где он стоит в памяти, а другие - по глубокому равенству, в то время как некоторым атрибутам может быть что-то совершенно другое. Нам бы хотелось, чтобы это был «четвертый тип равенства», очень хороший, часто называемый в литературе семантическим равенством . Вещи равны, если они равны, в нашем домене. =)

Итак, мы можем вернуться к вашему вопросу:

  

Есть ли какое-то важное преимущество в том, что я просто не пропущу, или   представляется разумным, что поведение по умолчанию должно быть логическим равенством,   и по умолчанию возвращается к эталонному равенству, если логического равенства не существует   для класса?

Что мы имеем в виду, когда пишем 'a == b' на любом языке? В идеале это всегда должно быть одно и то же: семантическое равенство. Но это невозможно.

Одним из основных соображений является то, что, по крайней мере, для простых типов, таких как числа, мы ожидаем, что после присвоения того же значения две переменные равны. См. Ниже:

var a = 1;
var b = a;
if (a == b){
    ...
}
a = 3;
b = 3;
if (a == b) {
    ...
}

В этом случае мы ожидаем, что «a равно b» в обоих утверждениях. Все остальное было бы безумным. Большинство (если не все) языков следуют этому соглашению. Поэтому, с простыми типами (aka values), мы знаем, как достичь семантического равенства. С объектами это может быть нечто совершенно другое. См. Ниже:

var a = new Something(1);
var b = a;
if (a == b){
    ...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
    ...
}

Мы ожидаем, что первое «если» всегда будет истинным. Но что вы ожидаете от второго «если»? Это действительно зависит. Может ли «DoSomething» изменить (семантическое) равенство a и b?

Проблема с семантическим равенством заключается в том, что он не может автоматически генерироваться компилятором для объектов, и это не очевидно из присваиваний . Необходимо предоставить механизм для определения семантического равенства. В объектно-ориентированных языках этот механизм является наследуемым методом: равно . Читая кусок кода OO, мы не ожидаем, что метод будет иметь такую ​​же точную реализацию во всех классах. Мы привыкли к наследованию и перегрузке.

С операторами, однако, мы ожидаем такого же поведения.Когда вы видите 'a == b', вы должны ожидать того же типа равенства (от 4 выше) во всех ситуациях. Итак, стремясь к консистенции , разработчики языков использовали ссылочное равенство для всех типов. Это не должно зависеть от того, переопределил ли программист метод или нет.

PS: Язык Dee немного отличается от Java и C #: оператор equals означает неглубокое равенство для простых типов и семантическое равенство для пользовательских классов (с ответственностью за реализацию операции =, лежащей с пользователем, - по умолчанию нет предоставлена). Поскольку для простых типов неглубокое равенство всегда является семантическим равенством, язык согласован. Однако цена, которую он платит, заключается в том, что оператор equals по умолчанию не определен для пользовательских типов. Вы должны его реализовать. И, иногда, это просто скучно.

ответил Hbas 12 J0000006Europe/Moscow 2013, 23:42:49

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132