从List <x>转换为List <y>?</y> </x>的语法较短

时间:2011-02-25 08:51:25

标签: c# list casting ienumerable

我知道可以将一个项目列表从一种类型转换为另一种类型(假设您的对象有一个公共静态显式运算符方法来执行转换),一次一个:

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

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

但是不可能一次投出整个列表吗?例如,

ListOfY = (List<Y>)ListOfX;

7 个答案:

答案 0 :(得分:432)

如果X真的可以投放到Y,您应该可以使用

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

有些事情需要注意(对评论者来说是H / T!)

答案 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();