Embora possamos herdar da classe / interface base, por que não podemos declarar o List<>
uso da mesma classe / interface?
interface A
{ }
class B : A
{ }
class C : B
{ }
class Test
{
static void Main(string[] args)
{
A a = new C(); // OK
List<A> listOfA = new List<C>(); // compiler Error
}
}
Existe uma maneira de contornar?
Respostas:
A maneira de fazer isso funcionar é iterar sobre a lista e converter os elementos. Isso pode ser feito usando o ConvertAll:
Você também pode usar o Linq:
fonte
ConvertAll
ouCast
?Antes de tudo, pare de usar nomes de classe impossíveis de entender como A, B, C. Use Animal, Mamífero, Girafa ou Comida, Fruta, Laranja ou algo onde os relacionamentos sejam claros.
Sua pergunta então é "por que não posso atribuir uma lista de girafas a uma variável do tipo de lista de animais, pois posso atribuir uma girafa a uma variável do tipo animal?"
A resposta é: suponha que você possa. O que poderia então dar errado?
Bem, você pode adicionar um tigre a uma lista de animais. Suponha que permitamos que você coloque uma lista de girafas em uma variável que contém uma lista de animais. Então você tenta adicionar um tigre a essa lista. O que acontece? Deseja que a lista de girafas contenha um tigre? Você quer um acidente? ou você deseja que o compilador o proteja da falha, tornando a atribuição ilegal em primeiro lugar?
Nós escolhemos o último.
Esse tipo de conversão é chamado de conversão "covariante". No C # 4, permitiremos que você faça conversões covariantes em interfaces e delegados quando se sabe que a conversão é sempre segura . Veja meus artigos de blog sobre covariância e contravariância para obter detalhes. (Haverá um novo sobre este tópico na segunda e na quinta-feira desta semana.)
fonte
IEnumerable
vez de umList
? ou seja:List<Animal> listAnimals = listGiraffes as List<Animal>;
não é possível, masIEnumerable<Animal> eAnimals = listGiraffes as IEnumerable<Animal>
funciona.IEnumerable<T>
eIEnumerator<T>
são marcados como seguros para covariância, e o compilador verificou isso.Para citar a grande explicação de Eric
Mas e se você quiser escolher uma falha de tempo de execução em vez de um erro de compilação? Você normalmente usaria o Cast <> ou o ConvertAll <>, mas terá 2 problemas: Ele criará uma cópia da lista. Se você adicionar ou remover algo da nova lista, isso não será refletido na lista original. Em segundo lugar, há uma grande penalidade de desempenho e memória, pois ela cria uma nova lista com os objetos existentes.
Eu tive o mesmo problema e, portanto, criei uma classe de wrapper que pode converter uma lista genérica sem criar uma lista totalmente nova.
Na pergunta original, você poderia usar:
e aqui a classe wrapper (+ um método de extensão CastList para facilitar o uso)
fonte
Se você usar
IEnumerable
, ele funcionará (pelo menos no C # 4.0, eu não tentei versões anteriores). Este é apenas um elenco, é claro, ainda será uma lista.Ao invés de -
List<A> listOfA = new List<C>(); // compiler Error
No código original da pergunta, use -
IEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
fonte
List<A> listOfA = new List<C>(); // compiler Error
no código original da pergunta, digiteIEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
Na medida em que não funciona, pode ser útil entender covariância e contravariância .
Apenas para mostrar por que isso não deve funcionar, aqui está uma alteração no código que você forneceu:
Isso deve funcionar? O primeiro item da lista é do tipo "B", mas o tipo do item DerivedList é C.
Agora, suponha que realmente queremos apenas criar uma função genérica que opere em uma lista de algum tipo que implemente A, mas não nos importamos com o tipo:
fonte
Você só pode transmitir para listas somente leitura. Por exemplo:
E você não pode fazer isso para listas que suportam salvar elementos. O motivo é:
E agora? Lembre-se de que listObject e listString são a mesma lista, portanto, listString agora tem um elemento de objeto - não deve ser possível e não é.
fonte
Pessoalmente, gosto de criar bibliotecas com extensões para as classes
fonte
Como o C # não permite esse tipo de
herançaconversão no momento .fonte
Esta é uma extensão da brilhante resposta de BigJim .
No meu caso, eu tive uma
NodeBase
aula com umChildren
dicionário e precisava de uma maneira generosa de fazer pesquisas O (1) das crianças. Eu estava tentando retornar um campo de dicionário privado no getter deChildren
, então obviamente eu queria evitar cópias / iterações caras. Portanto, usei o código de Bigjim para converter oDictionary<whatever specific type>
em um genéricoDictionary<NodeBase>
:Isso funcionou bem. No entanto, acabei encontrando limitações não relacionadas e acabei criando um
FindChild()
método abstrato na classe base que faria as pesquisas. Como se viu, isso eliminou a necessidade do dicionário fundido em primeiro lugar. (Consegui substituí-lo por um simplesIEnumerable
para meus propósitos.)Portanto, a pergunta que você pode fazer (especialmente se o desempenho é um problema que o impede de usar
.Cast<>
ou.ConvertAll<>
) é:"Preciso realmente converter a coleção inteira ou posso usar um método abstrato para manter o conhecimento especial necessário para executar a tarefa e, assim, evitar o acesso direto à coleção?"
Às vezes, a solução mais simples é a melhor.
fonte
Você também pode usar o
System.Runtime.CompilerServices.Unsafe
pacote NuGet para criar uma referência ao mesmoList
:Dada a amostra acima, você pode acessar as
Hammer
instâncias existentes na lista usando atools
variável AdicionarTool
instâncias à lista gera umaArrayTypeMismatchException
exceção porque faztools
referência à mesma variável quehammers
.fonte
Eu li todo esse tópico e só quero apontar o que me parece uma inconsistência.
O compilador impede que você faça a atribuição com Listas:
Mas o compilador está perfeitamente bem com matrizes:
O argumento sobre se a tarefa é conhecida como segura se desfaz aqui. A tarefa que fiz com a matriz não é segura . Para provar isso, se eu seguir com isso:
Recebo uma exceção de tempo de execução "ArrayTypeMismatchException". Como se explica isso? Se o compilador realmente quer me impedir de fazer algo estúpido, deveria ter me impedido de fazer a atribuição da matriz.
fonte