¿el operador condicional null no funciona con tipos nullables?


Estoy escribiendo un fragmento de código en c#6 y por alguna extraña razón esto funciona

var value = objectThatMayBeNull?.property;

Pero esto no:

int value = nullableInt?.Value;

Por no funciona quiero decir que obtengo un error de compilación diciendo Cannot resolve symbol 'Value'. ¿Alguna idea de por qué el operador condicional nulo ?. no está funcionando?

Author: Patrick Hofman, 2015-08-04

7 answers

Bien, he hecho algunas reflexiones y pruebas. Esto es lo que sucede:

int value = nullableInt?.Value;

Da este mensaje de error al compilar:

El tipo 'int `no contiene una definición para'Value'

Eso significa que ? 'convierte' el int? en el valor real int. Esto es efectivamente lo mismo que:

int value = nullableInt ?? default(int);

El resultado es un entero, que no tiene un Value, obviamente.

Bien, podría esto ayudar?

int value = nullableInt?;

No, esa sintaxis no es permitir.

Entonces, ¿qué? Solo sigue usando .GetValueOrDefault() para este caso.

int value = nullableInt.GetValueOrDefault();
 25
Author: Patrick Hofman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 14:22:35

La razón de esto es que acceder al valor con un operador condicional nulo sería inútil:

  • Cuando se aplica x?.p donde p es un tipo de valor no nullable T, el resultado es de tipo T?. De la misma manera, el resultado de la operación nullableInt?.Value debe ser nullable.
  • Cuando su Nullable<T> tiene un valor, el resultado de nullableInt?.Value sería el mismo que el propio valor
  • Cuando su Nullable<T> no tiene un valor, el resultado sería null, que es, de nuevo, lo mismo que el valor en sí.

Aunque no tiene sentido acceder a Value con el operador ?., sí tiene sentido acceder a otras propiedades de tipos de valores nullables. El operador trabaja consistentemente con tipos de valor nullable y con tipos de referencia, por lo que estas dos implementaciones producen un comportamiento idéntico:

class PointClass {
    public int X { get; }
    public int Y { get; }
    public PointClass(int x, int y) { X = x; Y = y; }
}
struct PointStruct {
    public int X { get; }
    public int Y { get; }
    public PointStruct(int x, int y) { X = x; Y = y; }
}
...
PointClass pc = ...
PointStruct? ps = ...
int? x = pc?.X;
int? y = ps?.Y;

En el caso de un nullable struct el operador le permite acceder a una propiedad del tipo subyacente PointStruct, y agrega nullability a la resultado de la misma manera que lo hace para propiedades no nullables del tipo de referencia PointClass.

 16
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 14:50:53

Con respecto a los tipos nullables, el operador ?. está diciendo if not null, use the wrapped value. Así que para un nullable int, si el nullable tiene valor 8, el resultado de ?. sería 8, no el nullable que contiene 8. Dado que Value no es una propiedad de un int, se obtiene un error.

Entonces, el ejemplo de intentar usar la propiedad Value falla correctamente, pero lo siguiente funcionaría, {[18]]}

var x = nullableInt?.ToString();

Considere el operador de coalescencia nula, ??.

var x = nullableInt ?? 0;

Aquí, el operador dice: if null, return 0, otherwise return the value inside the nullable, que en este caso es un int. El operador ?. está funcionando de manera similar con respecto a extraer el contenido de la nullable.

Para su ejemplo específico, debe usar el operador ?? y un valor predeterminado apropiado en lugar del operador ?..

 6
Author: Jeff Yates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 14:28:45

Básicamente estoy de acuerdo con las otras respuestas. Esperaba que el comportamiento observado pudiera ser respaldado por algún tipo de documentación autorizada.

Dado que no puedo encontrar la especificación de C# 6.0 en ningún lugar (¿ya está disponible?), lo más cercano que encontré a" documentación " son las Notas de Diseño del Lenguaje C# para el 3 de febrero de 2014. Suponiendo que la información que se encuentra allí todavía refleja el estado actual de las cosas, aquí están las partes relevantes que explican formalmente el observado comportamiento.

La semántica es como aplicar el operador ternario a una comprobación de igualdad nula, un literal nulo y una aplicación del operador sin pregunta, excepto que la expresión se evalúa solo una vez:

e?.m(…)   =>   ((e == null) ? null : e0.m(…))
e?.x      =>   ((e == null) ? null : e0.x)
e?.$x     =>   ((e == null) ? null : e0.$x)
e?[…]     =>   ((e == null) ? null : e0[…])

Donde e0 es lo mismo que e, excepto si e es de un tipo de valor nullable, en cuyo caso e0 es e.Value.

Aplicando esa última regla a:

nullableInt?.Value

... el equivalente semántico la expresión se convierte en:

((nullableInt == null) ? null : nullableInt.Value.Value)

Claramente, nullableInt.Value.Value no se puede compilar, y eso es lo que has observado.

En cuanto a por qué se tomó la decisión de diseño de aplicar esa regla especial a tipos nullables específicamente, creo que la respuesta de dasblinkenlight cubre eso muy bien, así que no lo repetiré aquí.


Además, debo mencionar que, incluso si, hipotéticamente, no teníamos esta regla especial para los tipos que aceptan valores null, y la expresión nullableInt?.Value compila y se comportan como originalmente pensamiento...

// let's pretend that it actually gets converted to this...
((nullableInt == null) ? null : nullableInt.Value)

Aún así, la siguiente declaración de su pregunta sería inválida y produciría un error de compilación:

int value = nullableInt?.Value; // still would not compile

La razón por La que todavía no funciona es porque el tipo de la nullableInt?.Value expresión sería int?, no int. Por lo tanto, tendría que cambiar el tipo de la variable value a int?.

Esto también está cubierto formalmente en las Notas de Diseño del Lenguaje C# de para Feb 3, 2014:

El tipo de resultado depende de el tipo T del lado derecho del operador subyacente:

  • Si T es (se sabe que es) un tipo de referencia, el tipo de la expresión es T
  • Si T es (se sabe que es) un tipo de valor no nullable, el tipo de la expresión es T?
  • Si T es (se sabe que es) un tipo de valor nullable, el tipo de la expresión es T
  • De lo contrario (es decir, si no se sabe si T es una referencia o un valor type) la expresión es un error en tiempo de compilación.

Pero si se ve obligado a escribir lo siguiente para compilarlo:

int? value = nullableInt?.Value;

... entonces parece bastante inútil, y no sería diferente de simplemente hacer:

int? value = nullableInt;

Como otros han señalado, en su caso, probablemente quiso usar el operador de coalescencia nula ?? todo el tiempo, no el operador condicional nulo ?..

 4
Author: sstan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-08 05:45:49

El operador condicional null también desenvuelve la variable nullable. Así que después de la "?."operador, la propiedad "Value" ya no es necesaria.

Escribí un post que va más en detalle sobre cómo me encontré con esto. En caso de que te estés preguntando

Http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/

 0
Author: Min,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-11 09:29:24

Simplemente porque (basado en la respuesta de sstan anterior)

var value = objectThatMayBeNull?.property;

Es evaluado por el compilador como

var value = (objectThatMayBeNull == null) ? null : objectThatMayBeNull.property

Y

int value = nullableInt?.Value;

Me gusta

int value = (nullableInt == null) ? null : nullableInt.Value.Value;

Cuando nullableInt.Value.Value es Cannot resolve symbol 'Value' error de sintaxis!

 0
Author: honzakuzel1989,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-06 11:12:50

int no tiene una propiedad Value.

Considere:

var value = obj?.Property

Es equivalente a:

value = obj == null ? null : obj.Property;

Eso no tiene sentido con int y por lo tanto no con int? vía ?.

El antiguo GetValueOrDefault() tiene sentido con int?.

O para el caso, ya que ? tiene que devolver algo nullable, simplemente:

int? value = nullableInt;
 -1
Author: Jon Hanna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 14:21:00