2009-11-05 52 views
16

可能重复:
Puzzling Enumerable.Cast InvalidCastExceptionEnumerable.Cast <T>扩展方法无法从int转换为long,为什么?

嗨,

我只是发现了一些与Enumerable.Cast<T>扩展方法很奇怪......它似乎不能施放从intlong ,尽管这个演员是完全合法的。

下面的代码失败,并InvalidCastException

 foreach (var item in Enumerable.Range(0,10).Cast<long>()) 
     { 
      Console.WriteLine(item); 
     } 

但是这个代码,我认为是等同的,不工作:

 foreach (var item in Enumerable.Range(0,10).Select(i => (long)i)) 
     { 
      Console.WriteLine(item); 
     } 

任何人都可以解释这种行为?我看着与反射铸造法的代码,但反射不能interprete迭代块,所以这是很费解......

+2

可能重复:http://stackoverflow.com/questions/445471 – 2009-11-06 00:06:22

+0

是的,你”对吧......我错过了我的搜索 – 2009-11-06 01:16:28

+2

我很惊讶,没有一个答案实际上回答了这个问题*为什么*。答案是因为从int到long **的转换不是cast **。这是一个*转换*。不幸的是,C#对这两种语言使用相同的语法,因为它只会混淆人们(显然)。您也不能使用'.Cast ()'调用自定义的显式转换运算符,因为这也不是转换。 – Timwi 2010-08-27 08:07:53

回答

16

Cast相关线路:

this.<>2__current = (TResult)this.<obj>5__ab; 

我们可以模仿这使用下面的代码:

int foo = 1; 
long bar = Cast<long>(foo); //oh noes! 

T Cast<T>(object input) 
{ 
    return (T)input; 
} 

这也失败了。这里的关键是,在演员角度,这是一个对象。不是一个整数。这失败了,因为我们只能从对象中取消装箱到我们想要的确切类型。我们正在从对象 - 这可能是一个长盒子,但事实并非如此。这是一个盒装诠释。 Eric Lippert discussed this on his blog

我们已经决定拆箱只能拆箱到确切类型。如果你想打电话,做所有咕缓慢的方法,它的可用 - 你可以随时拨打转换...

在你的代码工作,你不处理装箱的int(对象) ,你有一个int。

+11

确实。请注意,Cast的早期版本序列运算符*得到了错误*并且意外地允许像这样的转换*成功*在设计时它们应该失败。 (它也非常非常缓慢)。这是最糟糕的错误情况,为了使代码正确行为,您必须潜在地破坏现有客户。我们取得了成功;代码现在正确和快速,不是过分宽松和缓慢,但我们感觉真的很糟糕。我感觉特别糟糕,因为我代码审查了不正确的实现,并且在发布之前没有发现问题。抱歉! – 2009-11-06 00:03:45

+0

当然,我忘了拳击......完美的解释,谢谢! – 2009-11-06 00:04:33

+8

呜呼!我说了些什么,Eric Lippert说“确实”。刚做了我的月:D – 2009-11-06 00:04:36

8

与大多数其他LINQ扩展方法不同,Cast扩展了非通用接口IEnumerable,而不是IEnumerable<T>

这意味着由Range调用生成的int值由Cast呼叫的基础枚举,然后尝试将它们转换为long和失败,因为值只能是装箱的确切相同类型盒装。

您可以通过显式拳击int值模仿你的第二个循环相同的异常行为:

foreach (var item in Enumerable.Range(0, 10).Select(i => (long)(object)i)) 
{ 
    Console.WriteLine(item); 
} 
+0

准确!但雷克斯M是第一个,抱歉...谢谢! – 2009-11-06 00:07:16

+0

这是为什么更正确的解释。 +1 – 2009-11-06 00:31:29

1

的问题是,CastIterator的MoveNext箱的电流值,并试图把它拆箱为目标类型(其中盒装值不是正确的类型),因此在类型检查过程中拆箱失败。

参考信息:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.unbox_any.aspx

L_003c: ldarg.0 
    L_003d: ldarg.0 
    L_003e: ldfld class [mscorlib]System.Collections.IEnumerator System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<>7__wrapac 
    L_0043: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() 
    L_0048: stfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab 
    L_004d: ldarg.0 
    L_004e: ldarg.0 
    L_004f: ldfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab 
    L_0054: unbox.any !TResult 

的解决方法是使用一个选择()

相关问题