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

Tho*_*que 17 c# linq casting

可能重复:
令人困惑的Enumerable.Cast InvalidCastException

嗨,

我只是注意到Enumerable.Cast<T>扩展方法有些奇怪......似乎它无法转换intlong,即使这个演员是完全合法的.

以下代码失败InvalidCastException:

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

但是这个代码,我认为是相同的,确实有效:

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

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

Rex*_*x M 16

相关部分Cast:

 this.<>2__current = (TResult)this.<obj>5__ab;
Run Code Online (Sandbox Code Playgroud)

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

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

T Cast<T>(object input)
{
    return (T)input;
}
Run Code Online (Sandbox Code Playgroud)

哪个也失败了.这里的关键是在演员阵容中,它是一个对象.不是int.这失败了,因为我们只能从对象中取消打包到我们想要的确切类型.我们是从物体出发 - 这可能是盒装长,但事实并非如此.这是一个盒装的int.Eric Lippert在他的博客上对此进行了讨论:

我们已经决定取消装箱只能拆箱到确切的类型.如果你想调用那个可以做到这一切的慢速方法,它可用 - 你可以随时调用转换......

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

  • 多德.保持镇定. (12认同)
  • 确实.请注意,早期版本的Cast <T>序列运算符*出错了*并且意外地允许这样的转换*成功*在设计时它们应该失败.(而且它也非常非常慢.)这是最糟糕的错误情况,为了使代码正常运行,您必须潜在地破坏现有客户.我们接受了打击; 代码现在是正确而快速的,不是过于宽松和缓慢,但我们觉得它真的很糟糕.我觉得特别糟糕,因为我编写了错误的实现代码,并且在发布之前没有发现问题.抱歉! (11认同)
  • 哇噢!我说了些什么,Eric Lippert说"确实".刚做完我的月份:D (8认同)
  • @Eric Lippert:看起来你只需要习惯你新发现的名人身份.;) (2认同)

Luk*_*keH 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);
}
Run Code Online (Sandbox Code Playgroud)