Enumerable.Cast <t>扩展方法无法从int转换为long,为什么?</t>

时间:2009-11-05 23:42:57

标签: c# linq casting

  

可能重复:
  Puzzling Enumerable.Cast InvalidCastException

您好,

我刚注意到Enumerable.Cast<T>扩展方法有些奇怪......似乎它无法从int强制转换为long,即使此强制转换完全合法。

以下代码以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);
        }

任何人都可以解释这种行为吗?我用Reflector查看了Cast方法的代码,但是Reflector无法解释迭代器块,所以很难理解......

3 个答案:

答案 0 :(得分: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;
}

哪个也失败了。这里的关键是在演员阵容中,它是一个对象。不是int。这失败了,因为我们只能从对象中取消装箱到我们想要的确切类型。我们是从物体出发 - 这可能是盒装长,但事实并非如此。这是一个盒装的int。 Eric Lippert discussed this on his blog

  

我们已经决定取消装箱只能拆箱到确切的类型。如果你想调用那个可以完成所有操作的慢速方法,它可以使用 - 你总是可以调用转换......

在你的代码中,你没有处理盒装的int(一个对象),你有一个int。

答案 1 :(得分:10)

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

这意味着int调用生成的Range值由Cast调用的基础枚举器设置,然后尝试将它们转换为long并失败,因为值只能拆箱到完全相同的类型。

您可以通过明确装箱int值来模仿第二个循环中的相同异常行为:

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

答案 2 :(得分: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

解决方法是使用Select()