转换list对象的更短语法Y>?

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

List<Y> ListOfY = new List<Y>();


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

但是是否可以一次强制施放整个列表?例如,

ListOfY = (List<Y>)ListOfX;
215984 次浏览

直接强制转换var ListOfY = (List<Y>)ListOfX是不可能的,因为它需要List<T>类型的有限公司/逆变性,而这在每种情况下都不能保证。请继续阅读,看看这个铸造问题的解决方案。

虽然能够编写这样的代码看起来很正常:

List<Animal> animals = (List<Animal>) mammalList;

因为我们可以保证每个哺乳动物都是动物,这显然是一个错误:

List<Mammal> mammals = (List<Mammal>) animalList;

因为不是每一种动物都是哺乳动物。

但是,使用c# 3及以上版本,您可以使用

IEnumerable<Animal> animals = mammalList.Cast<Animal>();

这让选角变得容易一些。这在语法上等同于逐个添加代码,因为它使用显式强制转换将列表中的每个Mammal强制转换为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>([Converter from Y to T]);

如果X真的可以转换为Y,你应该能够使用

List<Y> listOfY = listOfX.Cast<Y>().ToList();

一些需要注意的事情(H/T评论者!)

  • 你必须包含using System.Linq;来获得这个扩展方法
  • 这将强制转换列表中的每个项——而不是列表本身。调用ToList()将创建一个新的List<Y>
  • 此方法不支持自定义转换运算符。(参见为什么Linq Cast<>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这样的可变集合不支持covariance,而不可变迭代器和集合可以。)

使用.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

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>()