== и равно. Почему сложность?

С оператором == я могу сделать это:

int a = 10;
int b = 10;
a==b //true

В этом случае С# принимает решение о компиляции, и никакое виртуальное поведение не вступает в игру.

Но если я использую:

object a = 10;
object b = 10;
a == b //false

Результат - false, потому что object - это класс (ссылочный тип) и a и b, каждый из которых ссылается на разные объекты в коробке.

Чтобы решить эту проблему, я должен сделать:

object a = 10;
object b = 10;
a.Equals (b) // True

Потому что Equals - это виртуальный метод, и он разрешен во время выполнения в зависимости от фактического типа объектов.

Мой вопрос:

Почему дизайнеры С# не избежали проблемы, создав == virtual и так функционально идентичные Equals?

3 ответа

Потому что == - это метод static, поэтому он не может быть виртуальным. Вероятно, это было сделано для лучшей обработки null.

Обратите внимание, что ваша дилемма не имеет никакого отношения к боксу, С# использует тип времени компиляции обоих параметров, чтобы определить, какая перегрузка * из == имеет смысл. Например, возьмите следующий пример.

object s1 = "Hello";
object s2 = new String('H', 'e', 'l', 'l', 'o');
s1 == s2; //False
(string)s1 == (string)s2; //True

* Перегрузка не совсем корректна, так как мы ищем более одной иерархии классов, но она достаточно близка семантически для обсуждения.


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

class Base {
 public int First;
}
class Derived {
 public int Last;
}
var me = Base{ First = "Alexei" };
var someone = Derived { First = "Alexei", Last = "Unknown" };

Теперь, если какой-то "==" является виртуальным в базовом классе, а не переопределен в производном классе, чем me == someone является истинным, что является неожиданным.

Также не забывайте, что С# не является единственным языком, который компилирует код для .Net runtime, поэтому поведение должно быть разумным для других языков, которые могут иметь разные синтаксические функции для подобных понятий.


Потому что тогда гораздо труднее определить, являются ли a и b фактически одним и тем же объектом. Мы могли бы это знать. Мы, скорее всего, захотим узнать, что если мы имеем дело с ними через переменные типа object. Идентичность - одна из значимых особенностей object qua object. Другими определениями равенства не являются.

С ссылочными типами мы начинаем с ==, Equals и GetHashCode, ссылаясь только на тождество. ReferenceEquals всегда ссылается только на тождество.

Когда мы, скорее всего, позаботимся о другом способе рассмотрения двух объектов типа, которые будут эквивалентными, мы переопределяем Equals, чтобы использовать его, и GetHashCode для соответствия.

Когда мы в основном мало заботимся об идентичности, мы также переопределяем ==. (пример: string).

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

Имея дело с переменными object, у нас есть хороший шанс, что мы заботимся об идентичности, поэтому у нас есть == и хороший шанс, что мы заботимся о Equals, чтобы у нас это было. Трудно думать о чем-то, что не просто мешало бы.

licensed under cc by-sa 3.0 with attribution.