Щоб додати точку Sweko:
Причина, чому ролях
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
НЕ представляється можливим тому , що List<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<>()
перевіряє, чи можна елемент перетворити на цільовий тип перед спробою перетворення та відфільтровує незмінні типи.)
для кожного
Також зауважте, що якщо ОП написала це замість: (зверніть увагу на явне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