Чтобы добавить к точке Sweko:
Причина, по которой составлен
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
невозможно, потому что List<T>
является инвариантом в типе T и, следовательно, не имеет значения, является ли X
производным от Y
) - это потому, что определен List<T>
как:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Обратите внимание, что в этом объявлении тип T
здесь не имеет дополнительных модификаторов дисперсии)
Однако, если изменяемые коллекции не требуются в вашем дизайне, для многих из неизменных коллекций возможен апскейтинг, возможен , например. при условии, что Giraffe
происходит от Animal
:
IEnumerable<Animal> animals = giraffes;
Это потому, что IEnumerable<T>
поддерживает ковариацию в T
- это имеет смысл, учитывая, что IEnumerable
подразумевает, что коллекция не может быть изменена, так как она не поддерживает методы для добавления или удаления элементов из коллекции. Обратите внимание на ключевое слово out
в объявлении IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Вот дальнейшее объяснение по той причине, что изменяемые коллекции, такие как List
, не могут поддерживать covariance
, тогда как неизменяемые итераторы и коллекции могут.)
Литье с .Cast<T>()
Как уже упоминалось, .Cast<T>()
можно применить к коллекции, чтобы спроецировать новую коллекцию элементов, приведенных к T, однако при этом будет получено InvalidCastException
, если приведение к одному или нескольким элементы невозможны (что было бы таким же поведением, как при явном приведении в цикле OP foreach
).
Фильтрация и литье с OfType<T>()
Если входной список содержит элементы разных несовместимых типов, можно избежать потенциала InvalidCastException
, используя .OfType<T>()
вместо .Cast<T>()
. (.OfType<>()
проверяет, можно ли преобразовать элемент в целевой тип перед попыткой преобразования, и отфильтровывает несовместимые типы.)
Еогеасп
Также обратите внимание, что если OP написал это вместо этого: (обратите внимание на явное Y y
в foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
что также будет предпринята попытка литья. Однако, если невозможно использовать приведение, InvalidCastException
приведет к.
Примеры
Например, учитывая простую (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) { }
}
При работе с коллекцией смешанных типов:
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();
Принимая во внимание:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
отфильтровывает только Слонов, т.е. зебры уничтожаются.
Re: Неявные операторы приведения
Без динамического пользовательские операторы преобразования используются только в время компиляции *, поэтому, даже если оператор преобразования между скажем, Zebra и Elephant был сделан доступным, описанное выше поведение во время выполнения подходов к преобразованию не изменится.
Если мы добавим оператор преобразования для преобразования Зебры в Слона:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Вместо этого, учитывая приведенный выше оператор преобразования, компилятор сможет изменить тип указанного ниже массива с Animal[]
на Elephant[]
, учитывая, что теперь зебры можно преобразовать в однородную коллекцию слонов:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Использование операторов неявного преобразования во время выполнения
* Как уже упоминал Эрик, оператор преобразования может быть доступен во время выполнения, прибегнув к 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