¿Por qué el incremento de Nullable <int> no genera una excepción?

98

¿Podría explicar por qué Console.WriteLine escribe una línea vacía ( Console.WriteLine(null)dame un error de compilación) y por qué no hay NullReferenceException (ni siquiera a+=1debería generarla)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Maxim Zhukov
fuente
Todos los tres operadores ++, +=y +han levantado variantes. Por lo tanto, las declaraciones a++;, a += 1;y a = a + 1;están todas permitidas. Cada producto null(sin excepción) si aes inicialmente null.
Jeppe Stig Nielsen
5
NullReferenceException? pero int?no es un Reference, es solo un intque puede tener nullvalor
Khaled.K

Respuestas:

124

Estás observando los efectos de un operador elevado .

De la sección 7.3.7 de la especificación C # 5:

Los operadores elevados permiten que los operadores predefinidos y definidos por el usuario que operan en tipos de valor que no aceptan valores NULL también se utilicen con formas que aceptan valores NULL de esos tipos. Los operadores elevados se construyen a partir de operadores predefinidos y definidos por el usuario que cumplen ciertos requisitos, como se describe a continuación:

  • Para los operadores unarios, + ++ - -- ! ~ existe una forma elevada de un operador si el operando y los tipos de resultado son tipos de valor que no aceptan valores NULL. La forma levantada se construye agregando un único ?modificador a los tipos de operando y resultado. El operador elevado produce un valor nulo si el operando es nulo. De lo contrario, el operador elevado desenvuelve el operando, aplica el operador subyacente y envuelve el resultado.

Básicamente, a++en este caso es una expresión con un resultado de null(comoint? ) y la variable se deja intacta.

Cuando usted llama

Console.WriteLine(a);

que está encuadrado object, lo que lo convierte en una referencia nula, que se imprime como una línea vacía.

Jon Skeet
fuente
3
¿Hay alguna forma de imprimir "nulo" sin usar Console.WriteLine(a == null ? "null" : a)?
Cole Johnson
5
@Cole Johnson: Console.WriteLine (un ?? "nulo"); :)
David Chappelle
2
@DavidChappelle Cuando aes de tipo int?, yo diría que a ?? "null"no encuentra un tipo común para los dos operandos. Necesita una pista que objectsea ​​del tipo común. Así que lanza cualquiera de los operandos a eso. El aestará en caja. Por ejemplo ((object)a) ?? "null"o a ?? (object)"null".
Jeppe Stig Nielsen
súper. ¿Puedes vincular a la especificación? ¿Podemos definir esto cuando sobrecargamos operadores en nuestras propias clases?
Phil
@teabaggs: No puedo vincular fácilmente en este momento, y el vínculo sería solo a un documento de Word (que puede encontrar fácilmente con una búsqueda). Obtiene automáticamente operadores elevados cuando define las sobrecargas de operadores, cuando corresponda.
Jon Skeet
116

La respuesta de Jon es correcta, pero agregaría algunas notas adicionales.

¿Por qué Console.WriteLine(null)da un error de compilación?

Hay 19 sobrecargas de Console.WriteLiney tres de ellas son aplicables a a null: la que lleva una string, la que lleva una char[]y la que lleva una object. C # no puede determinar a cuál de estos tres te refieres, por lo que da un error. Console.WriteLine((object)null)Sería legal porque ahora está claro.

¿Por qué Console.WriteLine(a)escribe una línea vacía?

aes un nulo int?. La resolución de sobrecarga elige la objectversión del método, por lo que int?se encuadra en una referencia nula. Entonces esto es básicamente lo mismo que Console.WriteLine((object)null), que escribe una línea vacía.

¿Por qué no hay NullReferenceExceptionen el incremento?

¿Dónde está la referencia nula que te preocupa? aes un valor nulo int?que no es un tipo de referencia para empezar. Recuerde, los tipos de valor que aceptan valores NULL son tipos de valor , no tipos de referencia , por lo que no espere que tengan semántica de referencia a menos que estén encuadrados en un tipo de referencia. No hay boxeo en la adición.

Eric Lippert
fuente
0

¿Estás incrementando nulo ???

int? a = null;
a++;

Esta declaración simplemente significa, null++es decir, nulo + 1.

Según este documento, un tipo que acepta valores NULL puede representar el rango correcto de valores para su tipo de valor subyacente, más un valor nulo adicional. A un valor Nullable, pronunciado "Nullable of Int32", se le puede asignar cualquier valor de -2147483648 a 2147483647, o se le puede asignar el valor nulo

Aquí está incrementando el valor nulo, luego también se convertirá en un valor nulo, no 0 o cualquier otro número entero.

¿Por qué se imprime en blanco en lugar de error?

cuando imprime un tipo que acepta valores NULL con valor nulo, se imprime en blanco en lugar de error porque está imprimiendo una variable, es decir, el valor de una ubicación de memoria. que puede ser nulo o cualquier entero.

Pero cuando intenta imprimir nulo usando Console.WriteLine(null), como nulo no es una variable, no se refiere a ninguna ubicación de memoria. Y por eso da error "NullReferenceException".

Entonces, ¿cómo puedes imprimir cualquier número entero usando Console.WriteLine(2);?

En este caso, 2 se almacenará en la memoria en una ubicación temporal y el puntero apunta a esa ubicación de la memoria para imprimir.

Laxmikant Dange
fuente