“CLR 检测到无效程序”将 Enumerable.ToDictionary 与扩展方法一起使用时
一位同事给了我一个有趣的代码示例,该示例在运行时崩溃并出现 InvalidProgramException(“CLR 检测到无效程序”)。
这个问题似乎发生在 JIT 时间,因为它编译得很好,但在调用带有“违规”行的方法之前抛出异常 - 我猜是因为它正在被 JIT 处理。
有问题的行正在调用 Enumerable.ToDictionary
并传入 Func
作为第二个参数。
如果 Func
参数是用 lambda 完全指定的,那么它就可以工作;如果指定为方法组,则失败。这两个肯定是等价的吗?
这让我(以及发现它的同事!)难住了——而且它看起来确实像是一个 JIT 错误。
[编辑:抱歉 - 我在代码示例中以错误的方式得到了通过和失败的情况 - 现在已更正(上面的描述是正确的)]
有人有解释吗?
using System;
using System.Linq;
internal class Program
{
private static void Main(string[] args)
{
Test.Try();
}
}
public class Test
{
public static readonly int[] integers = new[] { 1, 3, 5 };
public static void Try()
{
var line = new Line { A = 3, B = 5 };
// PASSES
var dict = integers.ToDictionary<int, int, decimal>(i => i, i => line.Compute(i));
// FAILS
//var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute);
Console.WriteLine(string.Join(" ", dict.Select(kv => kv.Key + "-" + kv.Value)));
}
}
public class Line
{
public decimal A;
public decimal B;
}
public static class SimpleCompute
{
public static decimal Compute(this Line line, int value)
{
return line.A*value + line.B;
}
}
A colleague has passed me an interesting code sample that crashes with an InvalidProgramException
("CLR detected an Invalid Program") when run.
The problem seems to occur at JIT time, in that this compiles fine but throws the exception just before the method with the "offending" line is called - I guess as it is being JIT'd.
The line in question is calling Enumerable.ToDictionary
and passing in a Func
as the second argument.
If the Func
argument is fully specified with a lambda it works; if it is specified as a method group, if fails. Surely these two are equivalent?
This has me stumped (and the colleague who discovered it!) - and it certainly seems like a JIT error.
[EDIT: Sorry - I got the pass and fail cases the wrong way round in the code sample - now corrected (description above was correct)]
Does anyone have an explanation?
using System;
using System.Linq;
internal class Program
{
private static void Main(string[] args)
{
Test.Try();
}
}
public class Test
{
public static readonly int[] integers = new[] { 1, 3, 5 };
public static void Try()
{
var line = new Line { A = 3, B = 5 };
// PASSES
var dict = integers.ToDictionary<int, int, decimal>(i => i, i => line.Compute(i));
// FAILS
//var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute);
Console.WriteLine(string.Join(" ", dict.Select(kv => kv.Key + "-" + kv.Value)));
}
}
public class Line
{
public decimal A;
public decimal B;
}
public static class SimpleCompute
{
public static decimal Compute(this Line line, int value)
{
return line.A*value + line.B;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
编译器错误。
有关信息,我有异步 CTP,这可能是相关的;
csc
reports: 4.0.30319.440似乎是有区别的:
所以让我们看看 Reflector:
vs
如果你查看损坏的版本,它只加载一个委托。编译器错误,基本上:
以上所有内容都是“检查缓存的
i => i
是否存在;如果不存在,则创建它;然后加载它”。它从不对第二个委托执行任何操作。因此,堆栈上没有足够的值来进行方法调用。Compiler bug.
For info, I have the async CTP, which might be related;
csc
reports: 4.0.30319.440Seems to be a difference between:
so let's look in reflector:
vs
If you look in the broken version, it only loads one delegate. Compiler bug, basically:
all of the above is "check whether the cached
i => i
exists; if not create it; then load it". It never does anything with the second delegate. Consequently, there aren't enough values on the stack to make the method call.