Как назначение нулевого литерала типу System.Nullable <T>обрабатывается .Net (C #)? - PullRequest
4 голосов
/ 03 апреля 2009

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

int? myInt = 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 имеет некоторый тип значения и не может быть нулевым.

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

Ответы [ 5 ]

8 голосов
/ 03 апреля 2009

утверждение:

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, а попытка получить его Value вызовет исключение InvalidOperationException. Если, конечно, вы не используете GetValueOrDefault.

3 голосов
/ 03 апреля 2009

На самом деле происходит следующее: когда вы присваиваете 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);

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

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

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

0 голосов
/ 19 апреля 2009

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

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

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

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

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

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

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

0 голосов
/ 03 апреля 2009

Что-то вроде:

public Nullable() {
    this.value = default(T);
    this.hasValue = false;
}
0 голосов
/ 03 апреля 2009

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

...