我知道可以将一列项目从一种类型强制转换为另一种类型(假设你的对象有一个公共静态显式操作符方法来执行强制转换),如下所示:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
但是是否可以一次强制施放整个列表?例如,
ListOfY = (List<Y>)ListOfX;
我知道可以将一列项目从一种类型强制转换为另一种类型(假设你的对象有一个公共静态显式操作符方法来执行强制转换),如下所示:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
但是是否可以一次强制施放整个列表?例如,
ListOfY = (List<Y>)ListOfX;
直接强制转换变量ListOfY = (List<Y>)ListOfX是不可能的,因为它需要List<T>类型的co/逆变,并且在每种情况下都不能保证。请继续阅读,看看这个铸造问题的解决方案。
虽然能够编写这样的代码看起来很正常:
List<Animal> animals = (List<Animal>) mammalList;
因为我们可以保证每个哺乳动物都是动物,这显然是一个错误:
List<Mammal> mammals = (List<Mammal>) animalList;
因为不是每一种动物都是哺乳动物。
但是,使用c# 3及以上版本,您可以使用
IEnumerable<Animal> animals = mammalList.Cast<Animal>();
这让选角变得容易一些。这在语法上等同于逐个添加代码,因为它使用显式强制转换将列表中的每个哺乳动物强制转换为一个Animal,如果强制转换不成功,则会失败。
如果您希望对强制转换/转换过程有更多的控制,可以使用List<T>类的ConvertAll方法,该方法可以使用提供的表达式来转换项。它还有一个额外的好处,它返回一个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
如果X真的可以转换为Y,你应该能够使用
List<Y> listOfY = listOfX.Cast<Y>().ToList();
一些需要注意的事情(H/T评论者!)
你必须包括使用System.Linq;来获得这个扩展方法 这将强制转换列表中的每个项——而不是列表本身。调用ToList()将创建一个新的List<Y>。 此方法不支持自定义转换运算符。(参见为什么Linq强制转换<> helper不能与隐式强制转换操作符一起工作?) 此方法不适用于具有显式操作符方法的对象(framework 4.0)
补充一下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意味着集合不能被更改,因为它不支持从集合中添加或删除元素的方法。注意IEnumerable<T>声明中的out关键字:
public interface IEnumerable<out T> : IEnumerable
(这里进一步解释了为什么像List这样的可变集合不支持协方差,而不可变迭代器和集合可以。)
使用.Cast<T>()进行强制转换
正如其他人所提到的,. cast <T>()可以应用于一个集合来投射一个转换为T的新元素集合,但是如果对一个或多个元素进行强制转换是不可能的,那么这样做将抛出InvalidCastException(这将与在OP的foreach循环中进行显式强制转换的行为相同)。
使用OfType<T>()进行过滤和强制转换
如果输入列表包含不同且不兼容的类型的元素,则可以通过使用. oftype <T>()而不是. cast <T>()来避免潜在的InvalidCastException。(. oftype <>()在尝试转换之前检查元素是否可以转换为目标类型,并过滤不兼容的类型。)
foreach
还要注意,如果OP写了这样的代码:(注意foreach中显式的Y Y)
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
只过滤掉大象——也就是说,斑马被淘汰了。
回复:隐式强制转换操作符
如果没有动态转换,用户定义的转换操作符只在编译时*使用,因此即使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[],因为斑马现在可以转换为大象的同质集合:
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
这不是这个问题的答案,但它可能对一些人有用:正如@SWeko所说,由于协方差和逆变性,List<X>不能在List<Y>中强制转换,但List<X>可以强制转换为IEnumerable<Y>,甚至使用隐式强制转换。
例子:
List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error
but
List<Y> ListOfY = new List<Y>();
IEnumerable<X> EnumerableOfX = ListOfY; // No issue
最大的优点是它不会在内存中创建一个新的列表。
dynamic data = List<x> val;
List<y> val2 = ((IEnumerable)data).Cast<y>().ToList();