从List <X>转换为List <Y>的语法较短?
我知道可以从一种types转换为另一种types的项目列表(假设你的对象有一个公共静态显式操作符方法来执行转换),如下所示:
List<Y> ListOfY = new List<Y>(); foreach(X x in ListOfX) ListOfY.Add((Y)x);
但是不可能一次列出整个清单吗? 例如,
ListOfY = (List<Y>)ListOfX;
如果X
真的可以投到Y
你应该能够使用
List<Y> listOfY = listOfX.Cast<Y>().ToList();
有些事情要注意(H / T给评论者!)
- 你必须包含
using System.Linq;
得到这个扩展方法 - 这将投射列表中的每个项目 – 而不是列表本身。 通过调用
ToList()
将创build一个新的List<Y>
。 - 此方法不支持自定义转换运算符。 (请参阅http://stackoverflow.com/questions/14523530/why-does-the-linq-cast-helper-not-work-with-the-implicit-cast-operator )
- 此方法不适用于具有显式运算符方法的对象(框架4.0)
直接转换var ListOfY = (List<Y>)ListOfX
是不可能的,因为它需要与 List<T>
types的共同/ var ListOfY = (List<Y>)ListOfX
,并且在任何情况下都不能得到保证。 请继续阅读,看看这个铸造问题的解决scheme。
虽然能够编写如下代码似乎是正常的:
List<Animal> animals = (List<Animal>) mammalList;
因为我们可以保证每个哺乳动物都是动物,这显然是一个错误:
List<Mammal> mammals = (List<Mammal>) animalList;
因为不是每个动物都是哺乳动物。
但是,使用C#3及以上版本,您可以使用
IEnumerable<Animal> animals = mammalList.Cast<Animal>();
这简化了铸造一点点。 这在语法上等同于你的一个一个添加代码,因为它使用一个明确的Mammal
来将列表中的每个Mammal
都转换成一个Animal
,并且如果强制转换不成功则会失败。
如果您更喜欢投射/转换过程,则可以使用List<T>
类的ConvertAll
方法,该方法可以使用提供的expression式来转换项目。 它有额外的好处,它返回一个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
你可以使用List.ConvertAll([从Y转换到T]);
要增加Sweko的观点:
投的原因
var listOfX = new List<X>(); ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
是不可能的,因为genericsList<T>
是不变的,因此X
是否从Y
导出并不重要)。
请看这里为什么像List
这样的可变集合不能支持covariance
。
但是,如果不需要可变集合 ,则可以使用诸如
IEnumerable<Animal> animals = giraffes;
是可能的 ,如果Giraffe
来源于Animal
,因为IEnumerable<T>
支持T
协方差 – 这是有道理的,因为IEnumerable
推断集合不能被改变。
使用.Cast<T>
投射
正如其他人所提到的, .Cast<T>
可以应用于一个集合,以投射到T的新元素集合,但是如果在一个或多个元素上投射是不可能的,那么这样做会抛出一个InvalidCastException
与在OP的foreach
循环中进行显式types转换相同的行为)。
使用OfType<T>
筛选和转换
如果input列表包含不同的,不兼容的types的元素,则可以通过使用.OfType<>
而不是.Cast<>
来避免潜在的InvalidCastException
。 ( .OfType<>
检查元素是否可以转换为目标types。)
使用foreach()进行types过滤
还要注意的是,如果OP已经写了:(注意在foreach
明确的Y y
)
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>
例子
例如,给定简单的(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) { } }
使用混合types的集合时:
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之间的转换操作符可用,转换方法的上述运行时行为也不会改变。
如果我们添加一个转换运算符来将Zebra转换为Elephant:
public class Zebra : Animal { public Zebra(string name) : base(name) { } public static implicit operator Elephant(Zebra z) { return new Elephant(z.Name); } }
相反,考虑到上述转换运算符,编译器将能够将下面的数组的types从Animal[]
更改为Elephant[]
,因为斑马现在可以转换为同类的大象集合:
var compilerInferredAnimals = new [] { new Zebra("Zed"), new Elephant("Ellie") };
*正如Eric所说,转换运算符可以通过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