Per aggiungere al punto di Sweko:
Il motivo per cui il cast
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
non è possibile perché List<T>
è invariante nel tipo T e quindi non importa se X
deriva da Y
) - questo perché List<T>
è definito come:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Si noti che in questa dichiarazione, digitare T
qui non ha modificatori di varianza aggiuntivi)
Tuttavia, se nel tuo progetto non sono richieste raccolte mutabili, è possibile effettuare l'upgrade a molte delle raccolte immutabili , ad esempio a condizione che Giraffe
derivi da Animal
:
IEnumerable<Animal> animals = giraffes;
Questo perché IEnumerable<T>
supporta la covarianza in T
- questo ha senso dato che IEnumerable
implica che la collezione non può essere cambiata, poiché non ha supporto per i metodi per aggiungere o rimuovere elementi dalla collezione. Nota la out
parola chiave nella dichiarazione di IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Ecco un'ulteriore spiegazione del motivo per cui le collezioni mutabili come List
non possono supportare covariance
, mentre gli iteratori e le raccolte immutabili possono farlo .)
Casting con .Cast<T>()
Come altri hanno già detto, .Cast<T>()
può essere applicato a una raccolta per proiettare una nuova raccolta di elementi proiettati su T, tuttavia in tal modo si genererà un InvalidCastException
cast se il cast su uno o più elementi non è possibile (il che sarebbe lo stesso comportamento del esplicito cast nel foreach
loop dell'OP ).
Filtraggio e trasmissione con OfType<T>()
Se l'elenco di input contiene elementi di tipi diversi e incompatibili, il potenziale InvalidCastException
può essere evitato utilizzando .OfType<T>()
invece di .Cast<T>()
. ( .OfType<>()
verifica se un elemento può essere convertito nel tipo di destinazione, prima di tentare la conversione, e filtra i tipi incompatibili).
per ciascuno
Si noti inoltre che se l'OP avesse scritto questo: (notare l' esplicitoY y
nel foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
che verrà anche tentato il casting. Tuttavia, se non è possibile lanciare un cast, si InvalidCastException
otterrà un risultato.
Esempi
Ad esempio, vista la gerarchia di classi semplice (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) { }
}
Quando si lavora con una raccolta di tipi misti:
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();
Mentre:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
filtra solo gli elefanti, ovvero le zebre vengono eliminate.
Ri: operatori cast impliciti
Senza un operatore dinamico, gli operatori di conversione definiti dall'utente vengono utilizzati solo in fase di compilazione *, quindi anche se un operatore di conversione tra dicesse Zebra ed Elephant fosse reso disponibile, il comportamento del runtime sopra descritto degli approcci alla conversione non cambierebbe.
Se aggiungiamo un operatore di conversione per convertire una Zebra in un elefante:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Invece, dato l'operatore di conversione sopra, il compilatore sarà in grado di cambiare il tipo dell'array sottostante da Animal[]
a Elephant[]
, dato che le Zebre possono ora essere convertite in una raccolta omogenea di Elefanti:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Utilizzo di operatori di conversione implicita in fase di esecuzione
* Come menzionato da Eric, è possibile accedere all'operatore di conversione in fase di esecuzione ricorrendo 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