Para adicionar ao ponto de Sweko:
A razão pela qual o elenco
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
isso não é possível porque List<T>
é invariável no Tipo T e, portanto, não importa se X
deriva de Y
) - isso ocorre porque List<T>
é definido como:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Observe que nesta declaração, digite T
aqui não possui modificadores de variação adicionais)
No entanto, se coleções mutáveis não forem necessárias em seu design, é possível fazer upcast em muitas das coleções imutáveis , por exemplo, desde que Giraffe
derivadas de Animal
:
IEnumerable<Animal> animals = giraffes;
Isso ocorre porque IEnumerable<T>
suporta covariância em T
- isso faz sentido, pois IEnumerable
implica que a coleção não pode ser alterada, pois não possui suporte para métodos para adicionar ou remover elementos da coleção. Observe a out
palavra - chave na declaração de IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Aqui está mais uma explicação do motivo pelo qual coleções mutáveis como List
não podem suportar covariance
, enquanto iteradores e coleções imutáveis podem.)
Fundição com .Cast<T>()
Como outros já mencionaram, .Cast<T>()
pode ser aplicado a uma coleção para projetar uma nova coleção de elementos convertidos para T, no entanto, isso será acionado InvalidCastException
se a conversão em um ou mais elementos não for possível (o que seria o mesmo comportamento que o explícito no foreach
loop do OP ).
Filtragem e conversão com OfType<T>()
Se a lista de entrada contiver elementos de tipos diferentes e incompatíveis, o potencial InvalidCastException
poderá ser evitado usando em .OfType<T>()
vez de .Cast<T>()
. ( .OfType<>()
verifica se um elemento pode ser convertido no tipo de destino, antes de tentar a conversão, e filtra tipos incompatíveis.)
para cada
Observe também que, se o OP tivesse escrito isso: (observe o explícitoY y
no foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
que o elenco também será tentado. No entanto, se nenhuma conversão for possível, InvalidCastException
haverá um resultado.
Exemplos
Por exemplo, dada a hierarquia de classes simples (C # 6):
public abstract class Animal
{
public string Name { get; }
protected Animal(string name) { Name = name; }
}
public class Elephant : Animal
{
public Elephant(string name) : base(name){}
}
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
}
Ao trabalhar com uma coleção de tipos mistos:
var mixedAnimals = new Animal[]
{
new Zebra("Zed"),
new Elephant("Ellie")
};
foreach(Animal animal in mixedAnimals)
{
// Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
castedAnimals.Add((Elephant)animal);
}
var castedAnimals = mixedAnimals.Cast<Elephant>()
// Also fails for Zed with `InvalidCastException
.ToList();
Enquanto que:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
filtra apenas os elefantes - ou seja, as zebras são eliminadas.
Operadores implícitos de elenco
Sem dinâmica, os operadores de conversão definidos pelo usuário são usados apenas em tempo de compilação *; portanto, mesmo que um operador de conversão entre o Zebra e o Elephant seja disponibilizado, o comportamento do tempo de execução acima das abordagens à conversão não mudaria.
Se adicionarmos um operador de conversão para converter uma Zebra em um elefante:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Em vez disso, dado o operador de conversão acima, o compilador poderá alterar o tipo da matriz abaixo de Animal[]
para Elephant[]
, pois as Zebras agora podem ser convertidas em uma coleção homogênea de elefantes:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Usando operadores de conversão implícitos em tempo de execução
* Conforme mencionado por Eric, no entanto, o operador de conversão pode ser acessado em tempo de execução, recorrendo a dynamic
:
var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
new Zebra("Zed"),
new Elephant("Ellie")
};
foreach (dynamic animal in mixedAnimals)
{
castedAnimals.Add(animal);
}
// Returns Zed, Ellie