Jim*_*mbo 216 c# ienumerable casting list
我知道可以将一个项目列表从一种类型转换为另一种类型(假设您的对象有一个公共静态显式运算符方法来执行转换),一次一个:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
Run Code Online (Sandbox Code Playgroud)
但是不可能一次投出整个列表吗?例如,
ListOfY = (List<Y>)ListOfX;
Run Code Online (Sandbox Code Playgroud)
Jam*_*iec 452
如果X真的可以施展给Y你应该可以使用
List<Y> listOfY = listOfX.Cast<Y>().ToList();
Run Code Online (Sandbox Code Playgroud)
有些事情需要注意(对评论者的H/T!)
using System.Linq;才能获得此扩展方法List<Y>将通过调用创建一个新的ToList().SWe*_*eko 91
直接铸造var ListOfY = (List<Y>)ListOfX是不可能的,因为它需要合作/逆变中的List<T>类型,而只是不能在任何情况下得到保证.请继续阅读以了解此铸造问题的解决方案.
虽然能够编写这样的代码似乎很正常:
List<Animal> animals = (List<Animal>) mammalList;
Run Code Online (Sandbox Code Playgroud)
因为我们可以保证每一只哺乳动物都是动物,这显然是一个错误:
List<Mammal> mammals = (List<Mammal>) animalList;
Run Code Online (Sandbox Code Playgroud)
因为不是每一种动物都是哺乳动物.
但是,使用C#3及以上版本,您可以使用
IEnumerable<Animal> animals = mammalList.Cast<Animal>();
Run Code Online (Sandbox Code Playgroud)
这简化了铸件.这在语法上等同于你的逐个添加代码,因为它使用显式强制转换将Mammal列表中的每个强制转换为a Animal,并且如果强制转换成功则将失败.
如果您想更好地控制转换/转换过程,可以使用类的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
Run Code Online (Sandbox Code Playgroud)
Stu*_*tLC 10
添加到Sweko的观点:
施法之所以如此
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
Run Code Online (Sandbox Code Playgroud)
是不可能的,因为List<T>它在类型T中是不变的,因此无论是否X从中派生出来Y- 这是因为List<T>定义为:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
Run Code Online (Sandbox Code Playgroud)
(注意,在此声明中,T此处键入没有其他方差修饰符)
但是,如果您的设计中不需要可变集合,则可以对许多不可变集合进行向上转换,例如,如果Giraffe派生自以下内容Animal:
IEnumerable<Animal> animals = giraffes;
Run Code Online (Sandbox Code Playgroud)
这是因为IEnumerable<T>支持协方差T- 这是有道理的,因为它IEnumerable意味着集合无法更改,因为它不支持从集合中添加或删除元素的方法.请注意out声明中的关键字IEnumerable<T>:
public interface IEnumerable<out T> : IEnumerable
Run Code Online (Sandbox Code Playgroud)
(这里进一步解释了为什么可变集合List不能支持covariance,而不可变迭代器和集合可以.)
铸造 .Cast<T>()
正如其他人所提到的,.Cast<T>()可以应用于集合来投射一个新的元素集合,这些集合被投射到T,但是这样做会抛出一个InvalidCastException如果不能在一个或多个元素上强制转换(这与执行显式的行为相同)在OP的foreach循环中投射).
过滤和铸造 OfType<T>()
如果输入列表包含不同的,不兼容的类型的元素,则InvalidCastException可以通过使用.OfType<T>()而不是使用来避免潜在的.Cast<T>().(.OfType<>()在尝试转换之前检查元素是否可以转换为目标类型,并过滤掉不可复制的类型.)
的foreach
还要注意的是,如果OP写了这个:(注意明确Y y的foreach)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
Run Code Online (Sandbox Code Playgroud)
也将尝试铸造.但是,如果不能进行强制转换,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) { }
}
Run Code Online (Sandbox Code Playgroud)
使用混合类型的集合时:
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();
Run Code Online (Sandbox Code Playgroud)
鉴于:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
Run Code Online (Sandbox Code Playgroud)
只过滤掉大象 - 即消除了斑马.
Re:隐式投射算子
如果没有动态,用户定义的转换运算符仅在编译时*使用,因此即使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);
}
}
Run Code Online (Sandbox Code Playgroud)
相反,给定上面的转换运算符,编译器将能够将以下数组的类型更改Animal[]为Elephant[],因为Zebras现在可以转换为同类的Elephants集合:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Run Code Online (Sandbox Code Playgroud)
在运行时使用隐式转换运算符
*正如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
Run Code Online (Sandbox Code Playgroud)
这不完全是这个问题的答案,但它可能对某些人有用:正如@SWeko 所说,由于协变和逆变,List<X>不能强制转换为,List<Y>但List<X>可以转换为IEnumerable<Y>,甚至可以使用隐式转换。
例子:
List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error
Run Code Online (Sandbox Code Playgroud)
但
List<Y> ListOfY = new List<Y>();
IEnumerable<X> EnumerableOfX = ListOfY; // No issue
Run Code Online (Sandbox Code Playgroud)
最大的优点是它不会在内存中创建新列表。
| 归档时间: |
|
| 查看次数: |
148531 次 |
| 最近记录: |