Более короткий синтаксис для кастинга из списка <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