Я знаю, что можно приводить список элементов из одного типа в другой (учитывая, что у вашего объекта есть публичный статический явный операторный метод для выполнения приведения) по очереди следующим образом:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
Но нельзя ли разыграть весь список за один раз? Например,
ListOfY = (List<Y>)ListOfX;
c#
list
casting
ienumerable
Джимбо
источник
источник
Ответы:
Если
X
действительно может быть приведен кY
вам, должен быть в состоянии использоватьНекоторые вещи, которые нужно знать (H / T для комментаторов!)
using System.Linq;
чтобы получить этот метод расширенияList<Y>
будет создано при вызовеToList()
.источник
Cast<T>
метод не поддерживает пользовательские операторы преобразования. Почему помощник Linq Cast не работает с неявным оператором Cast .Прямой бросок
var ListOfY = (List<Y>)ListOfX
не представляется возможным , поскольку для этого потребуется сотрудничество / контравариации вList<T>
типа, и что просто не может быть гарантировано в любом случае. Пожалуйста, прочитайте дальше, чтобы увидеть решения этой проблемы.Хотя кажется нормальным иметь возможность писать такой код:
поскольку мы можем гарантировать, что каждое млекопитающее будет животным, это, очевидно, ошибка:
Поскольку не каждое животное является млекопитающим.
Однако, используя C # 3 и выше, вы можете использовать
это немного облегчает кастинг. Это синтаксически эквивалентно вашему добавляемому по отдельности коду, так как он использует явное приведение для приведения каждого
Mammal
в списке кAnimal
и потерпит неудачу, если приведение не будет успешным.Если вам нравится больше контроля над процессом приведения / преобразования, вы можете использовать
ConvertAll
методList<T>
класса, который может использовать предоставленное выражение для преобразования элементов. Он имеет дополнительное преимущество, которое он возвращаетList
вместоIEnumerable
, так что нет.ToList()
необходимости.источник
Чтобы добавить к точке Sweko:
Причина, почему актеры
невозможно, потому что
List<T>
является инвариантом в типе T и, следовательно, не имеет значения, является ли оноX
производным отY
этого - это потому, чтоList<T>
определяется как:(Обратите внимание, что в этом объявлении тип
T
здесь не имеет дополнительных модификаторов дисперсии)Однако, если изменяемые коллекции не требуются в вашем дизайне, в вентиляционных шахтах на многие из неизменных коллекций, возможно , например , при условии , что
Giraffe
вытекает изAnimal
:Это потому, что
IEnumerable<T>
поддерживает ковариацию вT
- это имеет смысл, учитывая, что этоIEnumerable
означает, что коллекция не может быть изменена, так как она не поддерживает методы для добавления или удаления элементов из коллекции. Обратите внимание наout
ключевое слово в объявленииIEnumerable<T>
:( Вот дальнейшее объяснение причины, по которой изменяемые коллекции, например,
List
не могут поддерживатьcovariance
, тогда как неизменные итераторы и коллекции могут.)Кастинг с
.Cast<T>()
Как уже упоминалось,
.Cast<T>()
можно применить к коллекции, чтобы спроектировать новую коллекцию элементов, приведенных к T, однако при этом произойдет бросок,InvalidCastException
если приведение к одному или нескольким элементам невозможно (что будет таким же поведением, как при выполнении явного в ролях ОПforeach
).Фильтрация и кастинг с
OfType<T>()
Если входной список содержит элементы разных несовместимых типов, потенциал
InvalidCastException
можно избежать, используя.OfType<T>()
вместо.Cast<T>()
. (.OfType<>()
проверяет, можно ли преобразовать элемент в целевой тип перед попыткой преобразования, и отфильтровывает несовместимые типы.)для каждого
Также обратите внимание, что если OP написал это вместо: (обратите внимание на явное
Y y
вforeach
)что кастинг также будет предпринят. Однако, если невозможно использовать приведение, результат
InvalidCastException
будет.Примеры
Например, учитывая простую (C # 6) иерархию классов:
При работе с коллекцией смешанных типов:
В то время как:
отфильтровывает только слонов - т.е. зебры уничтожаются
Re: неявные операторы приведения
Без динамических определяемые пользователем операторы преобразования используются только во время компиляции *, поэтому, даже если оператор преобразования между скажем, Zebra и Elephant был сделан доступным, описанное выше поведение подходов к преобразованию во время выполнения не изменилось бы.
Если мы добавим оператор преобразования для преобразования зебры в слона:
Вместо этого, учитывая приведенный выше оператор преобразования, компилятор сможет изменить тип указанного ниже массива с
Animal[]
наElephant[]
, учитывая, что теперь зебры можно преобразовать в однородную коллекцию слонов:Использование операторов неявного преобразования во время выполнения
* Как отметил Эрик, оператор преобразования может быть доступен во время выполнения, прибегая к
dynamic
:источник
foreach
не фильтрует, но использование более производного типа в качестве переменной итерации приведет к тому, что компилятор попытается выполнить приведение типа, что приведет к сбою на первом элементе, который не соответствует.Ты можешь использовать
List<Y>.ConvertAll<T>([Converter from Y to T]);
источник
Это не совсем ответ на этот вопрос, но он может быть полезен для некоторых: как сказал @SWeko, благодаря ковариации и контравариантности
List<X>
не может быть брошенList<Y>
, ноList<X>
может быть брошенIEnumerable<Y>
, и даже неявным приведением.Пример:
но
Большим преимуществом является то, что он не создает новый список в памяти.
источник
источник