Почему при увеличении Nullable <int> не возникает исключение?

99

Не могли бы вы объяснить, почему Console.WriteLine записывает пустую строку ( Console.WriteLine(null)выдает ошибку компиляции) и почему нет NullReferenceException (даже a+=1не должно вызывать его)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Максим Жуков
источник
Все три оператора ++, +=и +подняли варианты. Следовательно, все утверждения a++;, a += 1;и a = a + 1;разрешены. Каждый производит null(без исключения) если aизначально null.
Йеппе Стиг Нильсен,
5
NullReferenceException? но int?это не а Reference, это просто то, intчто может иметь nullзначение
Khaled.K

Ответы:

124

Вы наблюдаете за действием поднятого оператора .

Из раздела 7.3.7 спецификации C # 5:

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

  • Для унарных операторов + ++ - -- ! ~ существует поднятая форма оператора, если оба типа операнда и результата являются типами значений, не допускающими значения NULL. Поднятая форма создается путем добавления единственного ?модификатора к типам операндов и результатов. Поднятый оператор производит нулевое значение, если операнд равен нулю. В противном случае оператор с поднятием разворачивает операнд, применяет базовый оператор и обертывает результат.

По сути, a++в этом случае это выражение с результатом null(как int?), а переменная остается нетронутой.

Когда ты звонишь

Console.WriteLine(a);

который помещается в рамку object, что преобразует его в пустую ссылку, которая печатается как пустая строка.

Джон Скит
источник
3
Есть ли способ вывести «нуль» без использования Console.WriteLine(a == null ? "null" : a)?
Коул Джонсон,
5
@ Кол Джонсон: Console.WriteLine ("нуль"); :)
Дэвид Чаппель
2
@DavidChappelle Когда aимеет тип int?, я бы сказал, a ?? "null"что не находит общего типа для двух операндов. Вам нужна objectобычная подсказка . Так что приведите к этому любой операнд. aБудет в штучной упаковке. Например ((object)a) ?? "null"или a ?? (object)"null".
Йеппе Стиг Нильсен,
супер. можно ссылку на спек? можем ли мы определить это, когда перегружаем операторы в наших собственных классах?
Фил
@teabaggs: Я не могу легко связать прямо сейчас, и ссылка будет просто на документ Word (который вы можете легко найти с помощью поиска). Вы автоматически получаете поднятые операторы, когда вы определяете перегрузки операторов, где это необходимо.
Джон Скит,
116

Ответ Джона правильный, но я бы добавил несколько дополнительных примечаний.

Почему Console.WriteLine(null)выдает ошибку компиляции?

Имеется 19 перегрузок, Console.WriteLineи три из них применимы к a null: одна принимает a string, другая принимает a char[]и третья принимает object. C # не может определить, какой из этих трех вы имеете в виду, поэтому выдает ошибку. Console.WriteLine((object)null)будет законным, потому что теперь это ясно.

почему Console.WriteLine(a)пишет пустую строку?

aявляется нулем int?. При разрешении перегрузки выбирается objectверсия метода, поэтому int?для ссылки используется пустая. Таким образом, это в основном то же самое Console.WriteLine((object)null), что записывает пустую строку.

Почему нет NullReferenceExceptionна инкременте?

Где пустая ссылка, которая вас беспокоит? a- это нуль, int?который изначально не является ссылочным типом! Помните, что типы значений, допускающие значение NULL, являются типами значений , а не ссылочными типами , поэтому не ожидайте, что они будут иметь ссылочную семантику, если они не упакованы в ссылочный тип. В дополнении бокса нет.

Эрик Липперт
источник
0

Вы увеличиваете ноль ???

int? a = null;
a++;

Этот оператор просто означает, null++например, null + 1.

Согласно этому документу тип, допускающий значение NULL, может представлять правильный диапазон значений для своего базового типа значения плюс дополнительное значение NULL. Nullable, произносится как «Nullable of Int32», может быть присвоено любое значение от -2147483648 до 2147483647, или оно может быть присвоено нулевое значение

Здесь вы увеличиваете значение null, тогда оно также станет нулевым значением, а не 0 или любым другим целым числом.

Почему вместо ошибки отображается пустой?

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

Но когда вы пытаетесь напечатать null с помощью Console.WriteLine(null), поскольку null не является переменной, поэтому он не ссылается ни на одну ячейку памяти. Следовательно, это дает ошибку "NullReferenceException".

Тогда как вы можете напечатать любое целое число, используя Console.WriteLine(2);??

В этом случае 2 попадет в память во временном месте, а указатель указывает на эту ячейку памяти для печати.

Лаксмикант Данге
источник