Чтобы добавить к точке 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
если приведение к одному или нескольким элементам невозможно (что будет таким же поведением, как при выполнении явного в ролях ОП 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