Более короткий синтаксис для приведения из списка в список?
Я знаю, что можно привести список элементов из одного типа в другой (учитывая, что ваш объект имеет открытый статический явный метод оператора для выполнения приведения) по одному за раз следующим образом:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
но разве нельзя бросить весь список за один раз? Например,
ListOfY = (List<Y>)ListOfX;
4 ответа:
если
Xдействительно можно привести кYвы должны быть в состоянии использоватьList<Y> listOfY = listOfX.Cast<Y>().ToList();некоторые вещи, чтобы быть в курсе (H / T для комментаторов!)
- необходимо указать
using System.Linq;чтобы получить этот метод расширения- это бросает каждый элемент в списке-не сам список. Новый
List<Y>будет создан вызовомToList().- этот метод не поддерживает пользовательские операторы преобразования. ( видеть http://stackoverflow.com/questions/14523530/why-does-the-linq-cast-helper-not-work-with-the-implicit-cast-operator )
- этот метод не работает для объекта, который имеет явный метод оператора (framework 4.0)
прямой бросок
var ListOfY = (List<Y>)ListOfXневозможно, потому что это потребует co / contravariance наList<T>тип, и что просто не может быть гарантировано в каждом случае. Пожалуйста, читайте дальше, чтобы увидеть решения этой проблемы литья.хотя это кажется нормальным, чтобы иметь возможность писать код, как это:
List<Animal> animals = (List<Animal>) mammalList;поскольку мы можем гарантировать, что каждое млекопитающее будет животным, это, очевидно, ошибка:
List<Mammal> mammals = (List<Mammal>) animalList;так как не каждое животное является млекопитающее.
однако, используя C# 3 и выше, вы можете использовать
IEnumerable<Animal> animals = mammalList.Cast<Animal>();это немного облегчает кастинг. Это синтаксически эквивалентно вашему индивидуальному добавлению кода, так как он использует явное приведение для приведения каждого
Mammalв списке кAnimal, и потерпит неудачу, если бросок не будет успешным.Если вам нравится больше контроля над процессом литья / преобразования, вы можете использовать
ConvertAllметодList<T>класс, который может использовать предоставленное выражение для преобразования элементов. Он имеет дополнительное преимущество, что он возвращаетList, вместоIEnumerable, так что нет.ToList()необходимо.List<object> o = new List<object>(); o.Add("one"); o.Add("two"); o.Add(3); IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds
добавить к точке 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здесь нет дополнительных модификаторов дисперсии)однако, если изменяемые коллекции не требуется в вашем дизайне, upcast на многих из неизменяемых коллекций,можно, например, при условии, что
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если бросок на одном или больше элементов невозможно (что было бы тем же поведением, что и выполнение явного приведения в OPforeachпетли).фильтрация и литье с
OfType<T>()если входной список содержит элементы разных, несовместимых типов, то потенциал
InvalidCastExceptionможно избежать с помощью.OfType<T>()вместо.Cast<T>(). (.OfType<>()проверяет, может ли элемент быть преобразован в целевой тип, перед попыткой преобразования, и отфильтровывает несовместимые типы.)foreach
также обратите внимание, что если бы 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: неявные операторы приведения
без динамических пользовательских операторов преобразования используются только в времени компиляции*, поэтому даже если оператор преобразования между say 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