为何List<int>调用Cast<long>会抛出InvalidCastException?
问题解答
先看你给出的代码:
static void Main(string[] args) { List<int> myList = Enumerable.Range(0, 100).ToList(); try { List<long> myListLong = myList.Cast<long>().ToList(); } catch (InvalidCastException e) { Console.WriteLine("Error"); } Console.ReadLine(); }
为什么会抛出InvalidCastException?
核心原因是LINQ的Cast<T>()方法并不处理数值类型之间的显式/隐式转换,它的设计目标是做两种特定转换:
- 把非泛型
IEnumerable(比如ArrayList)里的元素拆箱转换为指定的引用类型或值类型; - 对泛型
IEnumerable<TInput>做引用转换(比如从基类引用转换为派生类引用,前提是实际类型匹配),或者拆箱转换(比如把object类型的装箱int转换为int)。
你的场景里,myList是List<int>,实现了IEnumerable<int>。调用Cast<long>()时,方法会尝试对每个int元素执行CLR层面的直接强制类型转换——但这不是我们平时写的(long)intValue这种数值转换,而是CLR认可的类型强制转换逻辑。由于int和long是完全独立的值类型,不存在这种CLR层面的转换规则,所以就会抛出InvalidCastException。
是不是因为int和long都是结构体类型?
不是的。这个异常和两者都是结构体没有直接关联:
- 反例:如果有一个
List<object>里面装的是装箱的int,调用Cast<long>()同样会抛出异常,因为拆箱只能拆回原类型(int),不能直接拆成long; - 正面例子:如果是引用类型,比如
List<Animal>里面实际装的是Dog实例(Dog继承自Animal),调用Cast<Dog>()是完全可以正常工作的,这时候都是引用类型,但Cast能正确处理。
所以问题的本质是Cast<T>()不处理数值类型之间的数值转换,和是否是结构体无关。
正确的转换方式
如果你想把List<int>转成List<long>,应该用Select()方法来执行数值转换:
List<long> myListLong = myList.Select(x => (long)x).ToList();
Select()会遍历每个元素,执行你指定的显式数值转换,这才是处理这种场景的正确方式。
内容的提问来源于stack exchange,提问作者Martin Verjans
相关产品推荐
相关产品推荐

