O tipo de resultado obtido com o operador condicional em C#
-
26-09-2019 - |
Pergunta
Estou tentando usar o operador condicional, mas eu estou ficando pendurado sobre o tipo que pensa que o resultado deve ser.
Abaixo está um exemplo que eu tenho inventado para mostrar o problema que eu estou tendo:
class Program
{
public static void OutputDateTime(DateTime? datetime)
{
Console.WriteLine(datetime);
}
public static bool IsDateTimeHappy(DateTime datetime)
{
if (DateTime.Compare(datetime, DateTime.Parse("1/1")) == 0)
return true;
return false;
}
static void Main(string[] args)
{
DateTime myDateTime = DateTime.Now;
OutputDateTime(IsDateTimeHappy(myDateTime) ? null : myDateTime);
Console.ReadLine(); ^
} |
} |
// This line has the compile issue ---------------+
Na linha indicada acima, recebo o seguinte erro de compilação:
Tipo de expressão condicional não pode ser determinado porque não há nenhuma conversão implícita entre '< null >' e 'Sistema.DateTime'
Estou confuso porque o parâmetro é um tipo anulável (DateTime?).O que é necessário para converter?Se for null, então o uso que, se é uma data de tempo, então use-o.
Eu estava sob a impressão de que:
condition ? first_expression : second_expression;
foi o mesmo que:
if (condition)
first_expression;
else
second_expression;
Claramente, este não é o caso.O que é o raciocínio por trás disso?
(NOTA:Eu sei que se eu fizer "myDateTime" um nullable DateTime, então ele vai funcionar.Mas por que ele precisa?
Como eu disse anteriormente, este é um exemplo imaginário.No meu exemplo real "myDateTime" é um dos dados mapeados valor que não pode ser feito anulável.)
Solução
O compilador não infere o tipo de resultado do operador condicional do uso do resultado, mas dos tipos de seus argumentos. O compilador falha quando vê essa expressão porque não pode deduzir o tipo do resultado:
IsDateTimeHappy(myDateTime) ? null : myDateTime;
Desde null
e DateTime
Não são compatíveis, você precisa dizer ao compilador qual deve ser o tipo. Um elenco deve fazer o truque:
DateTime? x = IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime;
OutputDateTime(x);
Agora o compilador não terá problemas. Você também pode escrever o acima em uma linha, se preferir (mas eu provavelmente não faria isso):
OutputDateTime(IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime);
Eric Lippert tem um boa resposta Isso também é relevante aqui e entra em mais detalhes sobre como o compilador determina os tipos.
Outras dicas
O motivo é que o operador ternário espera que ambos os operandos sejam do mesmo tipo. Todo o operador é elaborado antes de ser atribuído a um resultado (neste caso passado para uma função), para que o compilador não possa saber qual é o tipo de resultado.
IsDateTimeHappy(myDateTime) ? null : myDateTime
No caso acima, não há caminho de conversão entre null
e DateTime
. Assim que você lança um deles para DateTime?
, o compilador pode converter o outro:
IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime
//OR
IsDateTimeHappy(myDateTime) ? null : (DateTime?)myDateTime
A linha de código do punho acima funciona porque o compilador pode converter DateTime
para DateTime?
através de um operador de conversão implícito:
//In Nullable<T>
public static implicit operator T?(T value);
A segunda linha funciona porque null
pode ser atribuído a DateTime?
Como o último é um tipo de referência.
A conversão implícita não é permitida pela declaração de devolução. Se você tinha
if (condition)
return first_expression;
else
return second_expression;
Então você estaria comparando maçãs com maçãs. E você não teria problemas - como afirmou.
No seu caso, você é alocado muito espaço na pilha para um tempo de dados - que é um tipo de valor não indicado. Então, você está fazendo uma declaração que não faz nenhum sentido para o compilador. Se você disser, eu vou passar por você um A
ou a B
, então o A
e a B
Precisa ser a mesma coisa. No seu caso, o B
nunca pode ser um A
.
Eu acho que isso também é respondida aqui:Tipos anuláveis e o operador ternário:por que é `?10 :null` proibido?
Espero que o que você precisava.=]
O que o compilador está dizendo é:
Se
IsDateTimeHappy(myDateTime)
éfalse
, então eu preciso retornar um valor do tipoDateTime
igual amyDateTime
. Se fortrue
, então eu preciso retornar um valor igual anull
, mas você não me disse que tipo deveria ser!
É por isso que a resposta de Mark é a solução. Depois de fornecer um elenco dizendo ao compilador que tipo de valor será retornado se a condição for true
, pode verificar se o true
e false
Os valores de retorno podem ser convertidos para (ou são do mesmo tipo.
Saúde Mark! ;-)
Ao invés de null
usar default(DateTime?)
E então os dois lados do ternário terão tipos compatíveis.