Sintaxe mais curta para conversão de uma lista para uma lista ?

Eu sei que é possível lançar uma lista de itens de um tipo para outro (dado que seu object tem um método de operador explícito estático público para fazer a conversão) um de cada vez da seguinte maneira:

List ListOfY = new List(); foreach(X x in ListOfX) ListOfY.Add((Y)x); 

Mas não é possível lançar a lista inteira de uma só vez? Por exemplo,

 ListOfY = (List)ListOfX; 

Se X realmente pode ser lançado para Y você deve ser capaz de usar

 List listOfY = listOfX.Cast().ToList(); 

Algumas coisas para estar ciente (H / T para comentaristas!)

O var ListOfY = (List)ListOfX cast cast direto var ListOfY = (List)ListOfX não é possível porque requereria co / contravariance do tipo List , e isso não pode ser garantido em todos os casos. Por favor, leia para ver as soluções para este problema de fundição.

Embora pareça normal poder escrever código como este:

 List animals = (List) mammalList; 

porque podemos garantir que todo mamífero será um animal, isso é obviamente um erro:

 List mammals = (List) animalList; 

já que nem todo animal é um mamífero.

No entanto, usando C # 3 e acima, você pode usar

 IEnumerable animals = mammalList.Cast(); 

Isso facilita o lançamento de um pouco. Isso é sintaticamente equivalente ao seu código de adição um por um, já que ele usa um casting explícito para conjurar cada Mammal na lista para um Animal e falhará se o lançamento não for bem-sucedido.

Se você gosta de mais controle sobre o processo de conversão / conversão, pode usar o método ConvertAll da class List , que pode usar uma expressão fornecida para converter os itens. Tem o benefício adicionado que retorna uma List , em vez de IEnumerable , portanto não .ToList() é necessário.

 List o = new List(); o.Add("one"); o.Add("two"); o.Add(3); IEnumerable s1 = o.Cast(); //fails on the 3rd item List s2 = o.ConvertAll(x => x.ToString()); //succeeds 

Para adicionar ao ponto de Sweko:

A razão pela qual o casting

 var listOfX = new List(); ListOf ys = (List)listOfX; // Compile error: Cannot implicitly cast X to Y 

não é possível porque a List é invariante no Tipo T e, portanto, não importa se o X deriva de Y ) – isso é porque a List é definida como:

 public class List : IList, ICollection, IEnumerable ... // Other interfaces 

(Observe que, nesta declaração, o tipo T não possui modificadores de variação adicionais)

No entanto, se collections mutáveis ​​não forem necessárias em seu design, é possível fazer uma atualização em muitas das collections imutáveis, por exemplo, desde que a Giraffe derive de Animal :

 IEnumerable animals = giraffes; 

Isso ocorre porque IEnumerable suporta covariância em T – isso faz sentido, uma vez que IEnumerable implica que a coleção não pode ser alterada, uma vez que não tem suporte para methods para adicionar ou remover elementos da coleção. Observe a palavra out chave out na declaração de IEnumerable :

 public interface IEnumerable : IEnumerable 

( Aqui está mais uma explicação para o motivo pelo qual collections mutáveis ​​como List não podem suportar covariance , enquanto iteradores e collections imutáveis ​​podem.)

Fundição com .Cast()

Como outros já mencionaram, .Cast() pode ser aplicado a uma coleção para projetar uma nova coleção de elementos lançados em T, no entanto, isso lançará uma InvalidCastException se a conversão em um ou mais elementos não for possível (o que seria ser o mesmo comportamento que fazer o casting explícito no loop foreach do OP).

Filtrando e OfType() com OfType()

Se a lista de input contiver elementos de tipos diferentes e incompatíveis, o potencial InvalidCastException pode ser evitado usando .OfType() vez de .Cast() . ( .OfType<>() verifica se um elemento pode ser convertido para o tipo de destino, antes de tentar a conversão, e filtra tipos incompatíveis.)

para cada

Note também que se o OP tivesse escrito isto: (note o Y y explícito no foreach )

 List ListOfY = new List(); foreach(Y y in ListOfX) { ListOfY.Add(y); } 

que o casting também será tentado. No entanto, se nenhum cast for possível, uma InvalidCastException resultará.

Exemplos

Por exemplo, dada a hierarquia de classs simples (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) { } } 

Ao trabalhar com uma coleção de tipos mistos:

 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() // Also fails for Zed with `InvalidCastException .ToList(); 

Enquanto que:

 var castedAnimals = mixedAnimals.OfType() .ToList(); // Ellie 

filtra apenas os elefantes – ou seja, as zebras são eliminadas.

Re: operadores de casting implícito

Sem operadores de conversão dynamics, definidos pelo usuário, são usados ​​somente em tempo de compilation *, portanto, mesmo se um operador de conversão entre Zebra e Elephant for disponibilizado, o comportamento acima do tempo de execução das abordagens de conversão não mudaria.

Se adicionarmos um operador de conversão para converter uma Zebra em um elefante:

 public class Zebra : Animal { public Zebra(string name) : base(name) { } public static implicit operator Elephant(Zebra z) { return new Elephant(z.Name); } } 

Em vez disso, dado o operador de conversão acima, o compilador poderá alterar o tipo do array abaixo de Animal[] para Elephant[] , dado que as Zebras podem agora ser convertidas em uma coleção homogênea de elefantes:

 var compilerInferredAnimals = new [] { new Zebra("Zed"), new Elephant("Ellie") }; 

Usando operadores de conversão implícita em tempo de execução

* Como mencionado por Eric, o operador de conversão pode, no entanto, ser acessado em tempo de execução, recorrendo à dynamic :

 var mixedAnimals = new Animal[] // ie Polymorphic collection { new Zebra("Zed"), new Elephant("Ellie") }; foreach (dynamic animal in mixedAnimals) { castedAnimals.Add(animal); } // Returns Zed, Ellie 

Você pode usar List.ConvertAll([Converter from Y to T]);