Как .Net (C#) обрабатывает присвоение нулевого литерала типу System.Nullable‹T›?

Мне было интересно, знает ли кто-нибудь, как компилятор С# обрабатывает следующее назначение:

int? myInt = null;

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

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

После вызова это попытается запустить вторичный конструктор:

public Nullable(T value) {
    this.value = value;
    this.hasValue = true; 
}

Вот где моя путаница вступает в игру... this.value имеет некоторый тип значения и не может быть нулевым.

Итак, кто-нибудь знает, как происходит эта "магия"... или я ошибаюсь, предполагая, что вызывается вторичный конструктор? Вызывается ли конструктор по умолчанию, потому что компилятор знает, что он не может сопоставить подпись второго конструктора с нулевым литералом (в результате чего myInt назначается новому «нулевому» объекту Nullable)?


person Albert Oldfield    schedule 02.04.2009    source источник


Ответы (5)


Заявление:

int? myInt = null;

Компилируется как:

  .locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    valuetype [mscorlib]System.Nullable`1<int32>

Что, в соответствии с MSDN означает «Инициализировать каждое поле типа значения по указанному адресу нулевой ссылкой или 0 соответствующего примитивного типа».

Так что здесь нет конструктора или преобразования. HasValue вернет false, и попытка получить его значение вызовет исключение InvalidOperationException. Если вы, конечно, не используете GetValueOrDefault.

person Jb Evain    schedule 02.04.2009
comment
Спасибо Jb Evain, надо было додуматься разобрать на IL. - person Albert Oldfield; 03.04.2009

Что на самом деле происходит, так это то, что когда вы назначаете null экземпляру типа, допускающего значение NULL, компилятор просто создает новый экземпляр T?, используя конструктор по умолчанию (инструкция initobj IL на Jb answer), поэтому следующие две строки эквивалентны:

int? a = null;
Nullable<int> b = new Nullable<int>();

object.Equals(a,b); // true

Поэтому вы не можете сделать это:

Nullable<int> c = new Nullable<int>(null);

Нечто подобное происходит, когда вы сравниваете тип с нулевым значением с нулевым:

if (a == null)
{
  // ...
}

За кулисами он просто вызывает свойство a.HasValue.

person Christian C. Salvadó    schedule 02.04.2009

Я ожидаю, что для .HasValue установлено значение false, а для .Value установлено значение default(T). , но я этого не проверял.

person Joel Coehoorn    schedule 02.04.2009

Что-то типа:

public Nullable() {
    this.value = default(T);
    this.hasValue = false;
}
person yfeldblum    schedule 02.04.2009

C# — это язык высокого уровня, который компилируется в IL.

С введением типов, допускающих значение NULL, стандарт C# изменился, поэтому поведение компилятора C# пришлось изменить, чтобы он обрабатывал новое правило, такое как "никакая структура, кроме Nullable, не может быть присвоено значение null".

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

По сути, если компилятор анализирует код C# и обнаруживает, что вы присваиваете структуре значение null, он выводит ошибку. Если он обнаружит, что вы присваиваете значение null структуре Nullable<T>, то он знает, как с этим справиться, и генерирует соответствующий IL.

Из стандарта С#:

13.7.1 Преобразования нулевого типа: "Существует неявное преобразование из нулевой тип (§11.2.7) в любой тип, допускающий значение null. Это преобразование создает нулевое значение (§12.2) данного типа, допускающего значение null».

12.2 Значения по умолчанию: "Значением по умолчанию для типа, допускающего значение NULL, является экземпляр, для которого свойство HasValue имеет значение false. Ссылка на свойство Value значения по умолчанию типа, допускающего значение NULL, приводит к исключению типа System.InvalidOperationException. Значение по умолчанию также известно как нулевое значение типа, допускающего значение NULL. Существует неявное преобразование из нулевого типа (§11.2.7) в любой тип, допускающий значение null, и это преобразование дает нулевое значение типа».

person Triynko    schedule 19.04.2009