Moldagem de IEnumerable para IEnumerable

StackOverflow https://stackoverflow.com/questions/609608

  •  03-07-2019
  •  | 
  •  

Pergunta

Recentemente eu encontrei um comportamento muito surpreendente em C #. Eu tinha um método que leva IEnumerable<Object> como um parâmetro e eu estava passando IEnumerable<string> mas não é possível. Enquanto em c # tudo pode ser upcast para Objeto de por que isso não é possível? É totalmente confuso para mim. Por favor, alguém me esclarecer sobre esta questão.

Foi útil?

Solução

O termo técnico para isso é que os genéricos são invariantes em C # 3.0 e anteriores. De C # 4.0 em diante, as obras do elenco.

O que significa invariável é que não existe uma relação entre dois tipos genéricos apenas porque seu tipo genérico parâmetros estão relacionados (ou seja, são sub ou supertipos um do outro).

No seu exemplo, não há nenhuma relação de digitação entre um IEnumerable<object> e um IEnumerable<string>, só porque string é um subtipo de objeto. Eles estão apenas considerados dois tipos completamente alheios, como uma corda e um int (eles ainda são ambos subtipos de objeto, mas tudo é)

Existem algumas soluções alternativas e exceções para esta questão você correr para.

Primeiro, você pode lançar cada corda individualmente para objeto, se você estiver usando .NET 3.0 você pode fazer isso usando o método de extensão Cast<T>(). Caso contrário, você pode usar um foreach e colocou o resultado em uma nova variável do tipo estático que deseja.

Em segundo lugar, as matrizes são uma excepção para o tipo de referência, isto é, passando em um tipo de cadeia [] para um objeto método acccepting [] tipos deve funcionar.

Outras dicas

Como outros apontaram, genéricos tipos são invariáveis. IEnumerable<T> poderia ser co-variante, mas C # atualmente não suporta variantes que especificam. C # 4.0 é esperado para suporte variantes então isso pode ser suportado no futuro.

Para contornar este agora você pode usar um Cast<object>() o método de extensão LINQ. Supondo que você tenha um método chamado Foo que leva um IEnumerable<object>>. Você pode chamá-lo assim,

Foo(stringEnumerable.Cast<object>());

A maneira mais fácil de passar IEnumerable<string> a função que requer IEnumerable<object> é através da conversão de função como esta:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

Quando C # 4 sai, isso não vai ser necessárias, porque ele vai apoiar covariância e contravariance.

Você deve estar usando

IEnumerable<T> 

Se você quer passar em diferentes tipos, em seguida, você pode consultar T para descobrir que tipo é.

Uma boa maneira de pensar sobre isso é de se perguntar "O que aconteceria se você pudesse fazer isso?". Veja o seguinte exemplo:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

Nós adicionamos apenas um inteiro para uma lista de strings. O compilador faz isso para preservar a segurança de tipo.

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