Problema de tipo anulable con?: Operador condicional

154

¿Podría alguien explicar por qué esto funciona en C # .NET 2.0:

    Nullable<DateTime> foo;
    if (true)
        foo = null;
    else
        foo = new DateTime(0);

... pero esto no:

    Nullable<DateTime> foo;
    foo = true ? null : new DateTime(0);

El último formulario me da un error de compilación "El tipo de expresión condicional no se puede determinar porque no hay conversión implícita entre '<null>' y 'System.DateTime'".

No es que no pueda usar el primero, pero el segundo estilo es más consistente con el resto de mi código.

Nick Gotch
fuente
12
¿Puedes ahorrarte mucho tipeo usando DateTime? en lugar de Nullable <DateTime>.
Stewart Johnson

Respuestas:

325

Esta pregunta ya se ha hecho muchas veces. El compilador te dice que no sabe cómo convertir nulla DateTime.

La solución es simple:

DateTime? foo;
foo = true ? (DateTime?)null : new DateTime(0);

Tenga en cuenta que Nullable<DateTime>se puede escribir lo DateTime?que le ahorrará un montón de mecanografía.

Stewart Johnson
fuente
Funciona lo suficientemente bien, pero ahora no puede anular el cheque foo, siempre tendrá un valor. Sin embargo, no hay forma de evitar esto, como dice MojoFilter "Es porque en un operador ternario, los dos valores deben ser del mismo tipo".
DilbertDave
@DilbertDave La información de la publicación de MojoFilter es incorrecta.
Mishax
44
Agregaría que el compilador intenta adivinar el tipo resultante de la operación ternaria, no mirando la variable a la que está asignada, sino mirando los operandos. Encuentra <null>y, en DateTimelugar de encontrar el tipo de ancestro común, solo trata de encontrar una conversión entre ellos. (Bit extra: C # reconoce un <null>tipo, es decir, el tipo de cada nullexpresión.)
IllidanS4 quiere que Monica regrese el
Si ya se ha preguntado varias veces, ¿dónde está el indicador de pregunta duplicada?
starmandeluxe
@starmandeluxe probablemente todos señalen aquí (al menos eso es como llegué aquí)
Scott Chamberlain
19

Para su información (Offtopic, pero ingenioso y relacionado con tipos anulables) tenemos un operador útil solo para tipos anulables llamado operador de fusión nula

??

Usado así:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
fuente
9
¿Cómo responde esto a su pregunta?
Stewart Johnson
3
Nick está tratando de asignar nulo a foo si alguna condición es verdadera. La fusión nula asignará DateTime (0) al valor si foo es nulo. Los dos no tienen ninguna relación.
Jeromy Irvine
44
De ahí el FYI, offtopic pero algo bueno para saber.
FlySwat
Ah ok. Es bastante útil saberlo.
Jeromy Irvine
8

Es porque en un operador ternario, los dos valores deben resolverse en el mismo tipo.

MojoFilter
fuente
10
No, no tienen que ser del mismo tipo. El segundo operando debe ser implícitamente convertible al tipo del tercer operando o al revés.
Mishax
3

Sé que esta pregunta se hizo en 2008 y ahora es 5 años después, pero la respuesta marcada como una respuesta no me satisface. La respuesta real es que DateTime es una estructura, y como estructura no es compatible con nulo. Tienes dos formas de resolver eso:

Primero, hacer que null sea compatible con DateTime (por ejemplo, ¿convertir nulo en DateTime? Como sugiere el caballero con 70 votos a favor, o convertir nulo en Object o ValueType).

El segundo es hacer que DateTime sea compatible con nulo (por ejemplo, ¿convertir DateTime a DateTime?).

Mishax
fuente
3

Otra solución similar a la aceptada es usar C # 's default palabra clave de . Si bien se define utilizando genéricos, en realidad es aplicable a cualquier tipo.

Ejemplo de uso aplicado a la pregunta del OP:

Nullable<DateTime> foo;
foo = true ? default(DateTime) : new DateTime(0);

Ejemplo de uso con la respuesta aceptada actual:

DateTime? foo;
foo = true ? default(DateTime) : new DateTime(0);

Además, mediante el uso default, no necesita especificar la variable nullablepara asignarle un nullvalor. El compilador asignará automáticamente el valor predeterminado del tipo de variable específico y no se encontrará ningún error. Ejemplo:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
muebles nuevos
fuente
13
No es cierto, default(DateTime)no es nulo, es " 1.1.0001 0:00:00", lo mismo que new DateTime(0).
IllidanS4 quiere que Mónica regrese el
@ IllidanS4, no dije que es igual a null, solo que al usarlo default()puedes asignarlo a un nullablevalor (como dice MSDN). Los ejemplos que muestro demuestran la versatilidad que se puede utilizar con Nullable<DateTime>, DateTime?y simplemente DateTime. Si cree que esto es incorrecto, ¿puede proporcionar un PoC donde estos fallan?
muebles nuevos
3
Bueno, el interrogador quería almacenar nullen la variable, no default(DateTime), así que esto es engañoso en el mejor de los casos. Esto no es "versátil" como implica, ya que la expresión en su conjunto todavía tiene el mismo tipo DateTime, y puede reemplazar default(DateTime)con new DateTime()y hará lo mismo. Tal vez default(DateTime?)es a lo que te refieres, ya que en realidad es igual a null.
IllidanS4 quiere que Monica regrese el