Почему оператор == работает для Nullable <T>, когда == не определен? - PullRequest
12 голосов
/ 26 января 2012

Я только что посмотрел на этот ответ , который содержит код для Nullable<T> из .NET Reflector, и заметил две вещи:

  1. Требуется явное преобразованиепри переходе от Nullable<T> к T.
  2. Оператор == не определен.

Учитывая эти два факта, меня удивляет, что это компилируется:

int? value = 10;
Assert.IsTrue(value == 10);

С кодом value == 10 либо value магическим образом преобразуется в int (следовательно, допускается использование оператора int == или оператор ==магически определено для Nullable<int>. (Или, я полагаю, менее вероятно, Reflector пропускает часть кода.)

Я ожидал бы выполнить одно из следующих действий:

Assert.IsTrue((value.Equals(10)); // works because Equals *is* defined
Assert.IsTrue(value.Value == 10); // works because == is defined for int
Assert.IsTrue((int?)value == 10); // works because of the explicit conversion

Это, конечно, работает, но == также работает, и это часть, которую я не понимаю.

Причина, по которой я это заметил и задаю этот вопрос, заключается в том, что я пытаюсь написать структуруэто работает примерно так же, как Nullable<T>. Я начал с кода Reflector, связанного выше, и только что сделал сомОчень незначительные модификации.К сожалению, мой CustomNullable<T> не работает так же.Я не могу сделать Assert.IsTrue(value == 10).Я получаю «Оператор == не может быть применен к операндам типа CustomNullable<int> и int».

Теперь, независимо от того, насколько незначительна модификация, я не ожидал, что смогу сделать ...

CustomNullable<T> value = null;

... потому что я понимаю, что за Nullable<T> стоит какая-то магия компилятора, позволяющая устанавливать значения на null, хотя Nullable<T> является структурой, но я хотел бы ожидайте, что я смогу имитировать все другие варианты поведения Nullable<T>, если мой код написан (почти) одинаково.

Может ли кто-нибудь пролить свет на то, как работают различные операторы Nullable<T>, когда ониПохоже, не будет определено?

Ответы [ 5 ]

28 голосов
/ 26 января 2012

Учитывая эти два факта, меня удивляет, что это компилируется

Учитывая только эти два факта, это удивительно.

Вот третий факт: в C #, большинство операторов «поднимаются до нуля» .

Под словом «поднято до нуля» я подразумеваю, что если вы скажете:

int? x = 1;
int? y = 2;
int? z = x + y;

тогда вы получите семантику "если x или y равно нулю, то z равно нулю. Если оба не равны нулю, то добавьте их значения, преобразуйте в обнуляемое значение и присвойте результат z."

То же самое относится и к равенству, хотя равенство немного странно, потому что в C # равенство все еще имеет только два значения. Чтобы быть правильно поднятым, равенство должно быть трехзначным: x == y должно быть null , если x или y равно нулю, и true или false, если x и y оба ненулевые. Вот как это работает в VB, но не в C #.

Я ожидаю, что смогу имитировать все другие варианты поведения Nullable<T>, если мой код написан (почти) одинаково.

Вам придется научиться жить с разочарованием, потому что ваши ожидания полностью не соответствуют реальности. Nullable<T> - это особый тип , и его магические свойства глубоко заложены в язык C # и среду выполнения. Например:

  • C # автоматически переводит операторов в nullable. Там нет никакого способа сказать "автоматически поднять операторов в MyNullable". Вы можете быть довольно близки, написав свои собственные пользовательские операторы.

  • C # имеет специальные правила для нулевых литералов - вы можете назначить их для переменных, допускающих значение NULL, и сравнить их со значениями NULL, а компилятор сгенерирует для них специальный код.

  • Семантика бокса nullables очень странная и запекается во время выполнения. Нет возможности подражать им.

  • Обнуляемая семантика для операторов is, as и объединения объединяется с языком.

  • Обнуляемые значения не удовлетворяют ограничению struct. Нет способа подражать этому.

  • и т. Д.

3 голосов
/ 26 января 2012

Хорошо, если вы можете использовать рефлектор, почему бы вам не скомпилировать этот код:

int? value = 10;
Console.WriteLine(value == 10);

, а затем открыть его в рефлекторе?Вы увидите это (обязательно выберите «Нет» в качестве версии .net для декомпиляции):

int? value;
int? CS$0$0000;
&value = new int?(10);
CS$0$0000 = value;
Console.WriteLine((&CS$0$0000.GetValueOrDefault() != 10) ? 0 : &CS$0$0000.HasValue);

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

1 голос
/ 26 января 2012

Nullable<T> имеет этот метод :

public static implicit operator T?(T value)
{
  return new T?(value);
}

Похоже, существует неявное преобразование из него 10 в Nullable<int> 10

== /! = работа для вас.Вы можете добавить

public static bool operator ==(MyNullable<T> left, MyNullable<T> right) {}
// together with:
public static implicit operator MyNullable<T>(T value) {}

, который должен поддерживать myNullable == 10 операцию

1 голос
/ 26 января 2012

Это зависит от языка.C # и Visual Basic испускают различный код при работе с операторами для типов значений Nullable.Чтобы понять это, вам нужно взглянуть на действительный код IL.

1 голос
/ 26 января 2012

Поскольку компилятор преобразует Nullable<T> в T и , тогда выполняет сравнение.

...