Inferência de tipos mais de IEnumerable
-
11-09-2019 - |
Pergunta
Existem alguns lugares já no estouro de pilha sobre esse tipo de coisa, mas não exatamente o mesmo - assim desculpas antecipadamente se isso é algo que já foi respondida
.Por que isso não funciona:
public class MyBase { }
public class MyUtils
{
public bool Foo<T> (T myObject) { return true; }
public bool Foo (MyBase myBaseObject) { return false; }
public void Go<T> (IEnumerable<T> items)
{
foreach (var item in items)
{
// this test fails
Assert.IsFalse (Foo (item));
}
}
}
Se eu chamar Go () acima e passar uma carga de MyBase objetos, cada chamada para Foo irá chamar o Foo genérico (), que retorna verdadeiro.
new MyUtils ().Go (new MyBase[] { new MyBase (), new MyBase () });
Por que não chamar a versão MyBase especializada em vez disso? Se eu chamar Foo (novo MyBase ()) diretamente, ele corretamente infere que chamada para fazer. Isso é por causa da falta de covariância para coleções em C # 3, ou estou apenas sendo boba e não fazendo isso corretamente?
Obrigado!
Isaac
Solução
Ele não chama a um "especializada", porque os choses compilador qual método chamar quando o programa (que, neste caso, é a função Go
) é compilado , não quando ele é executado.
Quando o compilador é compilar a função Go
, a única informação que tem é que há algum objeto do tipo T
. Ele não tem qualquer idéia que você pode em algum momento posterior fornecê-lo com um objeto do tipo MyBase
. A única opção que tem é escolher a sobrecarga Foo<T>
, e assim que coze que para o programa compilado.
Se você quiser um aplicativo para escolher sobrecargas em tempo de execução, e escolher o melhor sobrecarga, olhando para o objeto enquanto o aplicativo é executado, que é chamado de "expedição dinâmica", e é usado somente por linguagens dinâmicas como Ruby, Python, PHP, etc.
C # 3 é totalmente estática e não suporta isso. Você teria que escrever uma instrução if no seu código para verificar o tipo se você queria para trabalhar desta forma. C # 4, por outro lado, tem algum suporte dinâmico. Se você estivesse escrevendo este código em C # 4, você poderia declarar a função 'Go' da seguinte forma:
public void Go<T> (IEnumerable<dynamic> items)
Em seguida, ele iria usar expedição dinâmica em tempo de execução para escolher qual sobrecarga é chamado, e chamaria a sobrecarga especializada para tirar MyBase
Outras dicas
De acordo com as especificações C # (7.4.3.2), o método não-genérico é melhor do que o genérico, por isso deve ser selecionado.
Mas, eu acho que o genérico é captado no seu caso, porque ele é chamado dentro de um contexto de invocação genérica (o "foreach" loop).