【发布时间】:2015-03-06 10:10:14
【问题描述】:
背景
我接受这不是在正常代码执行期间可能发生的事情,但我在调试时发现了它,并认为分享它很有趣。
我认为这是由 JIT 编译器引起的,但欢迎任何进一步的想法。
我已经使用 VS2013 针对 4.5 和 4.5.1 框架复制了这个问题:
设置
要查看此异常Common Language Runtime Exceptions,必须启用:
DEBUG > Exceptions...
我已将问题的原因提炼为以下示例:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication6
{
public class Program
{
static void Main()
{
var myEnum = MyEnum.Good;
var list = new List<MyData>
{
new MyData{ Id = 1, Code = "1"},
new MyData{ Id = 2, Code = "2"},
new MyData{ Id = 3, Code = "3"}
};
// Evaluates to false
if (myEnum == MyEnum.Bad) // BREAK POINT
{
/*
* A first chance exception of type 'System.NullReferenceException' occurred in ConsoleApplication6.exe
Additional information: Object reference not set to an instance of an object.
*/
var x = new MyClass();
MyData result;
//// With this line the 'System.NullReferenceException' gets thrown in the line above:
result = list.FirstOrDefault(r => r.Code == x.Code);
//// But with this line, with 'x' not referenced, the code above runs ok:
//result = list.FirstOrDefault(r => r.Code == "x.Code");
}
}
}
public enum MyEnum
{
Good,
Bad
}
public class MyClass
{
public string Code { get; set; }
}
public class MyData
{
public int Id { get; set; }
public string Code { get; set; }
}
}
复制
在if (myEnum == MyEnum.Bad) 上放置一个断点并运行代码。
当断点被命中时,Set Next Statement(Ctrl+Shift+F10) 成为if 语句的左大括号和运行到:
接下来,注释 out 第一个 lamda 语句并注释 in 第二个 - 因此不使用 MyClass 实例。
重新运行进程(打断,强制进入if 语句并运行)。你会看到代码正常工作:
最后,注释 in 第一个 lamda 语句并注释 out 第二个 - 所以使用了 MyClass 实例 。然后将if语句的内容重构为新方法:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication6
{
public class Program
{
static void Main()
{
var myEnum = MyEnum.Good;
var list = new List<MyData>
{
new MyData{ Id = 1, Code = "1"},
new MyData{ Id = 2, Code = "2"},
new MyData{ Id = 3, Code = "3"}
};
// Evaluates to false
if (myEnum == MyEnum.Bad) // BREAK POINT
{
MyMethod(list);
}
}
private static void MyMethod(List<MyData> list)
{
// When the code is in this method, it works fine
var x = new MyClass();
MyData result;
result = list.FirstOrDefault(r => r.Code == x.Code);
}
}
public enum MyEnum
{
Good,
Bad
}
public class MyClass
{
public string Code { get; set; }
}
public class MyData
{
public int Id { get; set; }
public string Code { get; set; }
}
}
重新运行测试,一切正常:
结论?
我的假设是 JIT 编译器已将 lamda 优化为始终为空,并且在初始化实例之前运行了一些进一步优化的代码。
正如我之前提到的,这在生产代码中永远不会发生,但我很想知道发生了什么。
【问题讨论】: