Более короткий синтаксис для кастинга из списка <X> в список <Y>?
Я знаю, что он может отображать список элементов из одного типа в другой (учитывая, что ваш объект имеет открытый статический явный операторный метод для кастинга) по очереди:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
Но невозможно ли сразу перечислить весь список? Например,
ListOfY = (List<Y>)ListOfX;
Ответы
Ответ 1
Если X
действительно может быть добавлено к Y
, вы должны иметь возможность использовать
List<Y> listOfY = listOfX.Cast<Y>().ToList();
Некоторые вещи, о которых нужно знать (H/T для комментаторов!)
- Вы должны включить
using System.Linq;
, чтобы получить этот метод расширения
- Это добавляет каждый элемент в список, а не сам список. Новый
List<Y>
будет создан вызовом ToList()
.
- Этот метод не поддерживает пользовательские операции преобразования. (см. fooobar.com/questions/40146/...)
- Этот метод не работает для объекта с явным операторным методом (framework 4.0)
Ответ 2
Прямое литье var ListOfY = (List<Y>)ListOfX
невозможно, потому что для List<T>
требуется co/contravariance, и это просто" t гарантируется в каждом случае. Пожалуйста, прочитайте, чтобы увидеть решения этой проблемы кастинга.
Хотя кажется нормальным, что вы можете написать такой код:
List<Animal> animals = (List<Animal>) mammalList;
потому что мы можем гарантировать, что каждое млекопитающее будет животным, это, очевидно, ошибка:
List<Mammal> mammals = (List<Mammal>) animalList;
поскольку не каждое животное является млекопитающим.
Однако, используя С# 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
Ответ 3
Вы можете использовать List.ConvertAll([Конвертер от Y до T]);
Ответ 4
Чтобы добавить к точке 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
, не могут поддерживать covariance
. *
Однако, если изменяемые коллекции не требуются, upcast, например
IEnumerable<Animal> animals = giraffes;
возможно, если Giraffe
происходит от Animal
, учитывая, что IEnumerable<T>
поддерживает ковариацию T
- это имеет смысл, учитывая, что IEnumerable
указывает, что сбор не может быть изменен.
Кастинг с .Cast<T>
Как уже упоминалось, .Cast<T>
можно применить к коллекции, чтобы спроектировать новую коллекцию элементов, приложенных к T, однако поэтому будет выбрано значение InvalidCastException
, если наложение на один или несколько элементов невозможно (что было бы таким же, как при явном приведении в цикл OP foreach
).
Фильтрация и литье с помощью OfType<T>
Если список ввода содержит элементы разных, несовместимых типов, потенциал InvalidCastException
можно избежать, используя .OfType<>
вместо .Cast<>
. (.OfType<>
проверяет, может ли элемент быть преобразован в целевой тип.)
Использование foreach() для фильтрации типов
Заметим также, что если бы OP писал: (обратите внимание на явный Y y
в foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
что любой элемент, который не является Y
или который не может быть преобразован в Y
, будет пропущен и исключен из результирующего списка. т.е. foreach(Y y in ListOfX){ ... Add(y) }
эквивалентно ListOfX.OfType<Y>
<сильные > Примеры
Например, с учетом простой (С# 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();
В то время как:
foreach(Elephant animal in mixedAnimals)
{
castedAnimals.Add(animal);
}
// Ellie
и
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[]
, учитывая, что теперь Zebras могут быть преобразованы в однородный набор слонов:
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