Pergunta

public class Foo : IFooBarable {...}
public class Bar : IFooBarable {...}

Então, por que isso não vai compilar ...

int a = 1;
IFooBarable ting = a == 1 ? new Foo() : new Bar();

Mas isso vai ...

IFooBarable ting = a == 1 ? new Foo() : new Foo();
IFooBarable ting = a == 1 ? new Bar() : new Bar();
Foi útil?

Solução

O compilador primeiro tenta avaliar a expressão à direita:

? new Foo() : new Bar();

Não há conversão implícita entre esses dois, daí a mensagem de erro. Você consegue fazer isso:

IFooBarable ting = a == 1 ? (IFooBarable)(new Foo()) : (IFooBarable)(new Bar());

Outras dicas

Isso é abordado na seção 7.13 da especificação de idioma C#. Essencialmente, o que está matando esse cenário é que deve haver uma conversão implícita entre os tipos de 2 valores para os operandos ternários. Essa conversão é considerada na abdência do tipo de variável.

Então também Foo deve ser conversível para Bar ou vice-versa. Nem é um erro de compilação.

Os últimos dois funcionam porque eles consideram apenas 1 tipo (ou seja Foo ou Bar). Porque eles são do mesmo tipo que determinam o tipo de expressão é simples e funciona bem.

Apenas para adicionar um pouco às respostas corretas postadas aqui: Existem duas diretrizes de design que levam a essa especificação.

A primeira é que raciocinamos de "de dentro para fora". Quando voce diz

double x = 2 + y;

Primeiro, elaboramos o tipo de x, depois o tipo de 2, depois o tipo de y, depois o tipo de (2+y) e, finalmente, descobrimos se x e (2+y) têm tipos compatíveis. Mas não usamos o tipo de x para decidir qual é o tipo de 2, y ou 2+y.

A razão pela qual essa é uma boa regra é porque muitas vezes o tipo de "receptor" é exatamente o que estamos tentando descobrir:

void M(Foo f) {}
void M(Bar b) {}
...
M(x ? y : z);

O que devemos fazer aqui? Temos que descobrir o tipo de expressão condicional para fazer resolução de sobrecarga, a fim de determinar se isso vai fazer ou barrar. Portanto, não podemos usar O fato de que isso é, digamos, ir à nossa análise do tipo de expressão condicional! Isso é um problema de galinha e ovo.

A exceção a esta regra são as expressões lambda, que Faz Tire o tipo deles do contexto deles. Fazer esse recurso funcionar corretamente era incrivelmente complicado; veja meu série de blogs Sobre expressões lambda vs métodos anônimos, se você estiver interessado.

O segundo elemento é que nunca "magia" um tipo para você. Quando recebeu um monte de coisas das quais devemos deduzir um tipo, sempre deduzimos um tipo que estava realmente bem à nossa frente.

No seu exemplo, a análise é assim:

  • elaborar o tipo de consequência
  • resolver o tipo de alternativa
  • Encontre o melhor tipo que é compatível com a consequência e a alternativa
  • Certifique -se de que exista uma conversão do tipo de expressão condicional para o tipo de coisa que está usando a expressão condicional.

De acordo com o primeiro ponto, não raciocinamos fora para insensibilidade; Não usamos o fato de sabermos o tipo de variável que vamos para descobrir o tipo de expressão. Mas o interessante agora é que quando você tem

b ? new Cat() : new Dog()

Dizemos "o tipo de expressão condicional é o melhor tipo no conjunto {Cat, Dog}". Não dizemos "o tipo de expressão condicional é o melhor tipo compatível com gato e cachorro". Isso seria mamífero, mas não fazemos isso. Em vez disso, dizemos "o resultado deve ser algo que realmente vimos" e dessas duas opções, nem o vencedor claro. Se você disse

b ? (Animal) (new Cat()) : new Dog()

Então temos uma escolha entre animal e cachorro, e o animal é o vencedor claro.

Agora, observe que na verdade não implementamos corretamente a especificação C# ao fazer essa análise de tipo! Para detalhes do erro, veja meu artigo nele.

Porque o tipo de expressão condicional é sempre inferido a partir de suas duas partes, não da variável à qual o resultado deve ser aplicado. Essa inferência só funciona quando os tipos são iguais ou um é de referência compatível com o outro. Nesse caso, nenhum dos dois tipos é de referência compatível ao outro.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top