我知道可以一次将一种类型的项目列表转换为另一种类型(假设您的对象具有公共静态显式运算符方法进行转换),如下所示:
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;
Answers:
如果X
真的可以投给Y
你应该可以使用
List<Y> listOfY = listOfX.Cast<Y>().ToList();
需要注意的一些事情(向评论者致敬!)
using System.Linq;
以获得此扩展方法List<Y>
通过调用将创建一个新的ToList()
。Cast<T>
方法不支持自定义转换运算符。为什么Linq Cast帮助程序不能与隐式Cast操作符一起使用。
直接铸造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
,并且如果强制转换不成功将失败。
如果您希望对转换/转换过程进行更多控制,则可以使用类的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
要补充Sweko的观点:
之所以投
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
不可能是因为类型TList<T>
是in不变的,因此是否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>
支持in的协方差T
-考虑到这IEnumerable
意味着不能更改集合,这是有意义的,因为它不支持从集合中添加或删除元素的方法。注意以下out
声明中的关键字IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
(这里是进一步的解释,以说明为什么可变的collection之类List
不能支持covariance
,而不变的迭代器和collection可以支持。)
搭配 .Cast<T>()
正如其他人提到的那样,.Cast<T>()
可以将其应用于集合以投射强制转换为T的元素的新集合,但是InvalidCastException
如果无法对一个或多个元素进行强制转换,则这样做将抛出(与显式操作的行为相同)。投放到OP的foreach
循环中)。
过滤和投射 OfType<T>()
如果输入列表包含不同的,不兼容的类型的元素,则InvalidCastException
可以通过使用.OfType<T>()
代替来避免潜在的可能性.Cast<T>()
。(.OfType<>()
在尝试进行转换之前,检查是否可以将元素转换为目标类型,并过滤掉不兼容的类型。)
前言
还要注意的是,如果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
仅滤除大象-即消除了斑马。
回复:隐式强制转换运算符
如果没有动态的用户定义转换运算符,则仅在编译时使用 *,因此,即使说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转换为同类的Elephants集合:
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
foreach
不过滤,但是使用更多派生类型作为迭代变量将迫使编译器尝试进行Cast,这将在第一个不符合条件的元素上失败。
这并不完全是这个问题的答案,但它可能是一些有用的:如@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
最大的优点是它不会在内存中创建新列表。