我知道可以将一个项目列表从一种类型转换为另一种类型(假设您的对象有一个公共静态显式运算符方法来执行转换),一次一个:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
但是不可能一次投出整个列表吗?例如,
ListOfY = (List<Y>)ListOfX;
答案 0 :(得分:432)
如果X
真的可以投放到Y
,您应该可以使用
List<Y> listOfY = listOfX.Cast<Y>().ToList();
有些事情需要注意(对评论者来说是H / T!)
using System.Linq;
才能获得此扩展方法List<Y>
。ToList()
答案 1 :(得分:88)
直接广播var ListOfY = (List<Y>)ListOfX
是不可能的,因为它需要List<T>
类型的co/contravariance,并且在每种情况下都无法保证。请继续阅读以了解此铸造问题的解决方案。
虽然能够编写这样的代码似乎很正常:
List<Animal> animals = (List<Animal>) mammalList;
因为我们可以保证每只哺乳动物都是动物,这显然是一个错误:
List<Mammal> mammals = (List<Mammal>) animalList;
因为并非所有动物都是哺乳动物。
但是,使用C#3及更高版本,您可以使用
IEnumerable<Animal> animals = mammalList.Cast<Animal>();
这使得铸件变得轻松一点。这在语法上等同于您的逐个添加代码,因为它使用显式强制转换将列表中的每个Mammal
强制转换为Animal
,如果强制转换,则会失败。< / p>
如果您希望更好地控制转换/转换过程,可以使用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
答案 2 :(得分:9)
要添加到Sweko的观点:
施法的原因
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
是不可能的,因为List<T>
在类型T 中不变,因此X
是否来自Y
并不重要 - 这是因为List<T>
被定义为:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(请注意,在此声明中,类型T
此处没有其他方差修饰符)
但是,如果您的设计中不需要可变集合,那么许多不可变集合的上传,是可能的,例如前提是Giraffe
来自Animal
:
IEnumerable<Animal> animals = giraffes;
这是因为IEnumerable<T>
支持T
中的协方差 - 这是有道理的,因为IEnumerable
意味着集合无法更改,因为它不支持添加或删除元素的方法来自收藏。请注意out
声明中的IEnumerable<T>
关键字:
public interface IEnumerable<out T> : IEnumerable
(Here's further explanation,因为像List
这样的可变集合不能支持covariance
,而不可变的迭代器和集合可以。{/ p>
使用.Cast<T>()
正如其他人所提到的,.Cast<T>()
可以应用于集合来投射一个新的元素集合,但是如果对一个或多个元素进行强制转换,则会抛出InvalidCastException
。不可能(这与在OP的foreach
循环中进行显式转换的行为相同)。
使用OfType<T>()
如果输入列表包含不同的,不兼容的类型的元素,则可以使用.OfType<T>()
而不是InvalidCastException
来避免潜在的.Cast<T>()
。 (.OfType<>()
检查在尝试转换之前是否可以将元素转换为目标类型,并过滤掉不可复制的类型。)
<强>的foreach 强>
另请注意,如果OP已写入此内容:(请注意Y y
中的显式foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
也会尝试施法。但是,如果无法进行强制转换,则会产生InvalidCastException
。
<强>实施例强>
例如,给定简单的(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) { }
}
使用混合类型的集合时:
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();
鉴于:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
只过滤掉大象 - 即消除了斑马。
Re:隐式投射操作符
如果没有动态,用户定义的转换运算符仅用于compile-time *,因此即使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);
}
}
相反,给定上面的转换运算符,编译器将能够将下面数组的类型从Animal[]
更改为Elephant[]
,因为Zebras现在可以转换为同类集合象:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
在运行时使用隐式转化运算符
*正如Eric所说,转换运算符可以在运行时通过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
答案 3 :(得分:7)
您可以使用List<Y>.ConvertAll<T>([Converter from Y to T]);
答案 4 :(得分:1)
这并不是这个问题的答案,但可能对某些人有用:正如@SWeko所说,由于协方差和矛盾,List<X>
不能强制转换为List<Y>
,但是{ {1}}可以强制转换为List<X>
,甚至可以隐式强制转换。
示例:
IEnumerable<Y>
但是
List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error
最大的优点是它不会在内存中创建新列表。
答案 5 :(得分:0)
如果 X 派生自 Y,您也可以使用 ToList<T>
方法代替 Cast<T>
listOfX.ToList<Y>()
答案 6 :(得分:-2)
dynamic data = List<x> val;
List<y> val2 = ((IEnumerable)data).Cast<y>().ToList();