Por que incrementar Nullable <int> não lança uma exceção?

98

Você poderia explicar, por que Console.WriteLine escreve uma linha vazia ( Console.WriteLine(null)me dá um erro de compilação) e por que não há NullReferenceException (nem a+=1deveria levantá-la)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Maxim Zhukov
fonte
Todos os três operadores ++, +=e +ter levantado variantes. Portanto, as instruções a++;, a += 1;e a = a + 1;são permitidas. Cada produto null(sem exceção lançada) aé inicialmente null.
Jeppe Stig Nielsen
5
NullReferenceException? mas int?não é um Reference, é apenas um intque pode ter nullvalor
Khaled.K,

Respostas:

124

Você está observando os efeitos de um operador suspenso .

Da seção 7.3.7 da especificação C # 5:

Operadores ativados permitem que operadores predefinidos e definidos pelo usuário que operam em tipos de valor não anuláveis ​​também sejam usados ​​com formas anuláveis ​​desses tipos. Os operadores levantados são construídos a partir de operadores predefinidos e definidos pelo usuário que atendem a certos requisitos, conforme descrito a seguir:

  • Para os operadores unários, + ++ - -- ! ~ uma forma elevada de um operador existe se o operando e os tipos de resultado forem ambos tipos de valor não anuláveis. A forma elevada é construída adicionando um único ?modificador aos tipos de operando e resultado. O operador levantado produz um valor nulo se o operando for nulo. Caso contrário, o operador levantado desembrulha o operando, aplica o operador subjacente e quebra o resultado.

Então, basicamente, a++neste caso, é uma expressão com um resultado de null(as an int?) e a variável é deixada intacta.

Quando Você ligar

Console.WriteLine(a);

que está sendo encaixotado em object, o que o converte em uma referência nula, que é impressa como uma linha vazia.

Jon Skeet
fonte
3
Existe uma maneira de imprimir "null" sem usar Console.WriteLine(a == null ? "null" : a)?
Cole Johnson
5
@Cole Johnson: Console.WriteLine (a ?? "null"); :)
David Chappelle,
2
@DavidChappelle Quando aé do tipo int?, eu diria a ?? "null"que não encontra um tipo comum para os dois operandos. Você precisa de uma dica que objectseja do tipo comum. Portanto, converta qualquer operando para isso. O aserá encaixotado. Por exemplo ((object)a) ?? "null"ou a ?? (object)"null".
Jeppe Stig Nielsen
super. você pode ligar para a especificação? podemos definir isso quando sobrecarregamos operadores em nossas próprias classes?
Phil
@teabaggs: Não consigo criar um link facilmente agora, e o link seria apenas para um documento do Word (que você pode encontrar facilmente com uma pesquisa). Você obtém operadores içados automaticamente ao definir sobrecargas de operador, quando apropriado.
Jon Skeet
116

A resposta de Jon está correta, mas gostaria de acrescentar algumas notas adicionais.

Porque Console.WriteLine(null) dá um erro de compilação?

Existem 19 sobrecargas de Console.WriteLinee três delas são aplicáveis ​​a a null: a que tira a string, a que tira a char[]e a que tira a object. C # não pode determinar qual desses três você quer dizer, então ele dá um erro.Console.WriteLine((object)null)seria legal porque agora está claro.

porque Console.WriteLine(a) escreve uma linha vazia?

aé nulo int?. A resolução de sobrecarga escolhe a objectversão do método, portanto, o int?é encaixotado em uma referência nula. Então, isso é basicamente o mesmo queConsole.WriteLine((object)null) que escreve uma linha vazia.

Porque não há NullReferenceException no incremento?

Onde está a referência nula com a qual você está preocupado? aé um nulo int?que não é um tipo de referência para começar! Lembre-se de que os tipos de valor anulável são tipos de valor , não tipos de referência , portanto, não espere que eles tenham semântica de referência, a menos que sejam encaixotados em um tipo de referência. Não há boxe na adição.

Eric Lippert
fonte
0

Você está incrementando nulo ???

int? a = null;
a++;

Esta declaração significa simplesmente, null++ou seja, nulo + 1.

De acordo com este documento, um tipo anulável pode representar o intervalo correto de valores para seu tipo de valor subjacente, mais um valor nulo adicional. Um anulável, pronunciado "Anulável de Int32," pode receber qualquer valor de -2147483648 a 2147483647, ou pode ser atribuído o valor nulo

Aqui você está incrementando nulo, então também se tornará o valor nulo, não 0 ou qualquer outro inteiro.

Por que ele imprime em branco em vez de erro ??

quando você imprime um tipo anulável com valor nulo, ele imprime em branco em vez de erro porque você está imprimindo uma variável, ou seja, o valor de um local da memória. que pode ser nulo ou qualquer inteiro.

Mas quando você tenta imprimir null usando Console.WriteLine(null), as null não é uma variável, então não se refere a nenhum local da memória. E, portanto, dá erro "NullReferenceException".

Então, como você pode imprimir qualquer inteiro usando Console.WriteLine(2);??

Nesse caso, 2 ficará na memória em um local temporário e o ponteiro aponta para esse local de memória para imprimir.

Laxmikant Dange
fonte