Problema de tipo anulável com?: Operador condicional

154

Alguém poderia explicar por que isso funciona no C # .NET 2.0:

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

... mas isso não acontece:

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

O último formulário gera um erro de compilação "O tipo de expressão condicional não pode ser determinado porque não há conversão implícita entre '<null>' 'e' System.DateTime '."

Não que eu não possa usar o primeiro, mas o segundo estilo é mais consistente com o restante do meu código.

Nick Gotch
fonte
12
Você pode economizar bastante digitação usando o DateTime? em vez de Nullable <DateTime>.
Stewart Johnson

Respostas:

325

Esta pergunta já foi feita várias vezes. O compilador está lhe dizendo que não sabe como converter nullem DateTime.

A solução é simples:

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

Observe que Nullable<DateTime>pode ser escrito, o DateTime?que poupará bastante digitação.

Stewart Johnson
fonte
Funciona bem o suficiente, mas agora você não pode verificar nulo foo - ele sempre terá um valor. No entanto, não há maneira de contornar isso - como o MojoFilter diz: "É porque em um operador ternário, os dois valores devem ser do mesmo tipo".
precisa saber é o seguinte
@DilbertDave As informações da publicação de MojoFilter estão incorretas.
Mishax
4
Eu acrescentaria que o compilador tenta adivinhar o tipo resultante da operação ternária não olhando para a variável à qual está atribuído, mas olhando para os operandos. Ele encontra <null>e, em DateTimevez de encontrar o tipo de ancestral comum, apenas tenta encontrar uma conversão entre si. (Bit extra: C # reconhece um <null>tipo, ou seja, o tipo de cada nullexpressão.)
IllidanS4 quer Monica de volta
Se já foi perguntado várias vezes, onde está o sinalizador de pergunta duplicada?
Starmandeluxe
@starmandeluxe todos eles apontam provável aqui (pelo menos que eu como cheguei aqui)
Scott Chamberlain
19

FYI (Offtopic, mas bacana e relacionado a tipos anuláveis), temos um operador útil apenas para tipos anuláveis ​​chamado operador coalescente nulo

??

Usado assim:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
fonte
9
Como isso responde a sua pergunta?
Stewart Johnson
3
Nick está tentando atribuir nulo a foo se alguma condição for verdadeira. A coalescência nula atribuirá DateTime (0) ao valor se foo for nulo. Os dois são completamente independentes.
Jeromy Irvine
4
Daí a FYI, offtopic, mas uma coisa agradável de saber.
FlySwat 17/11/08
Ah ok. É bastante útil saber.
Jeromy Irvine
8

Isso ocorre porque em um operador ternário, os dois valores devem ser resolvidos para o mesmo tipo.

MojoFilter
fonte
10
Não, eles não precisam ser do mesmo tipo. O segundo operando deve ser implicitamente conversível no tipo do terceiro operando ou vice-versa.
Mishax
3

Eu sei que essa pergunta foi feita em 2008 e agora é cinco anos depois, mas a resposta marcada como resposta não me satisfaz. A resposta real é que DateTime é uma estrutura e, como uma estrutura, não é compatível com nulo. Você tem duas maneiras de resolver isso:

O primeiro é tornar o nulo compatível com o DateTime (por exemplo, converter nulo para DateTime? Como sugere o cavalheiro com 70 votos positivos, ou converter nulo para Object ou ValueType).

O segundo é tornar o DateTime compatível com null (por exemplo, converter DateTime para DateTime?).

Mishax
fonte
3

Outra solução semelhante à aceita é usar a defaultpalavra-chave do C # . Embora definido usando genéricos, é realmente aplicável a qualquer tipo.

Exemplo de uso aplicado à pergunta do OP:

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

Exemplo de uso com a resposta atual aceita:

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

Além disso, ao usar default, você não precisa especificar a variável como nullablepara atribuir um nullvalor a ela . O compilador atribuirá automaticamente o valor padrão do tipo de variável específico e nenhum erro será encontrado. Exemplo:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
newfurniturey
fonte
13
Não é verdade, default(DateTime)não é nulo, é " 1.1.0001 0:00:00", o mesmo que new DateTime(0).
IllidanS4 quer Monica de volta 04/11/14
@ IllidanS4, eu não disse que é igual null, só que usando default()você pode atribuí-lo a um nullablevalor (como o MSDN declara). Os exemplos mostram que demonstram a versatilidade que pode ser utilizado com Nullable<DateTime>, DateTime?e simplesmente DateTime. Se você acredita que isso está incorreto, você pode fornecer um PoC onde estes falham?
newfurniturey
3
Bem, o questionador quis armazenar nullna variável, não default(DateTime), então isso é enganoso na melhor das hipóteses. Isso não é "versátil", como você sugere, já que a expressão como um todo ainda tem o mesmo tipo - DateTimee você pode substituir default(DateTime)por new DateTime()e fará a mesma coisa. Talvez default(DateTime?)seja o que você quis dizer, já que isso é realmente igual a null.
IllidanS4 quer Monica de volta