我知道可以将一列项目从一种类型强制转换为另一种类型(假设你的对象有一个公共静态显式操作符方法来执行强制转换),如下所示:

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

您可以使用List<Y>。ConvertAll<T>([从Y到T的转换器]);


如果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();

如果X起源于Y,你也可以使用ToList<T>方法而不是Cast<T>方法

listOfX.ToList<Y>()