迫使“设置下一条语句”CLR System.NullReferenceException时到'如果'块 [英] CLR System.NullReferenceException when forcing 'Set Next Statement' into 'if' block

查看:247
本文介绍了迫使“设置下一条语句”CLR System.NullReferenceException时到'如果'块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我接受这是不是可能出现正常的code在执行过程中,但我发现它在调试时,认为这有趣的分享。

I accept this isn't something that can occur during normal code execution but I discovered it while debugging and thought it interesting to share.

我想,这是由JIT编译器,但欢迎任何进一步的想法。

I think this is caused by the JIT compiler, but would welcome any further thoughts.

我已经重复这个问题的使用VS2013 4.5和4.5.1的框架目标:

I have replicated this issue targeting the 4.5 and 4.5.1 framework using VS2013:

要看到这个异常公共语言运行库异常必须启用: DEBUG > 例外...

To see this exception Common Language Runtime Exceptions must be enabled: DEBUG > Exceptions...

我已经蒸了问题的原因,以下面的例子:

I have distilled the cause of the issue to the following example:

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; }
    }
}


复制

放在断点,如果(myEnum == MyEnum.Bad)并运行code。 当破发点被击中,设置下一条语句控制 + <大骨节病>移 + <大骨节病> F10 )是如果语句的左括号并运行至:


To Replicate

Place a breakpoint on if (myEnum == MyEnum.Bad) and run the code. When the break point is hit, Set Next Statement(Ctrl+Shift+F10) to be the opening brace of the if statement and run until:

接下来,评论的的第一LAMDA声明和评论的的第二个 - 这样的 MyClass的实例ISN t一起使用。 重新运行程序(打快攻,迫使进入如果语句和运行)。你会看到code正常工作:

Next, comment out the first lamda statement and comment in the second - so the MyClass instance isn't used. Rerun the process (hitting the break, forcing into the if statement and running). You'll see the code works correctly:

最后,评论的的第一LAMDA声明和评论的的第二个 - 这样的 MyClass的例如<强>是使用。然后重构如果语句的内容到一个新的方法:

Finally, comment in the first lamda statement and comment out the second - so the MyClass instance is used. Then refactor the contents of the if statement into a new method:

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; }
    }
}

重新运行测试,一切工作正常:

Rerun the test and everything works correctly:

我的假设是JIT编译器优化了LAMDA总是为空,有的进一步优化code运行之前,该实例被初始化。

My assumption is the JIT compiler has optimized out the lamda to always be null, and some further optimized code is running prior to the instance being initialized.

正如我pviously提到这不可能发生在生产中code,但我很想知道发生了什么事。$ P $

As I previously mentioned this could never happen in production code, but I would be interested to know what was happening.

推荐答案

这是一个pretty的不可避免的事故,不相关的优化。通过使用设置下一语句命令,你绕过的更多的code比你可以轻松地从源头code见。当你在看生成的机器code它只变得很明显。使用调试+的Windows +拆卸的断点。你会看到:

This is a pretty inevitable mishap, not related to optimization. By using the Set Next Statement command, you are bypassing more code than you can easily see from the source code. It only becomes obvious when you look at the generated machine code. Use Debug + Windows + Disassembly at the breakpoint. You'll see:

            // Evaluates to false
            if (myEnum == MyEnum.Bad) // BREAK POINT 
0000016c  cmp         dword ptr [ebp-3Ch],1 
00000170  setne       al 
00000173  movzx       eax,al 
00000176  mov         dword ptr [ebp-5Ch],eax 
00000179  cmp         dword ptr [ebp-5Ch],0 
0000017d  jne         00000209 
00000183  mov         ecx,2B02C6Ch               // <== You are bypassing this
00000188  call        FFD6FAE0 
0000018d  mov         dword ptr [ebp-7Ch],eax 
00000190  mov         ecx,dword ptr [ebp-7Ch] 
00000193  call        FFF0A190 
00000198  mov         eax,dword ptr [ebp-7Ch] 
0000019b  mov         dword ptr [ebp-48h],eax 
            {
0000019e  nop 
                /*
                 * 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();
0000019f  mov         ecx,2B02D04h             // And skipped to this
000001a4  call        FFD6FAE0 
// etc...

那么,什么是神秘的code?这不是任何你在程序中明确写道。您可以通过在反汇编窗口中使用设置下一语句命令找出。将它移动到地址 00000183 时,如果()语句后的第一个可执行code。开始步进,你会看到它在执行一类名为 ConsoleApplication1.Program 1所述的构造;&GT; c__DisplayClass5

So, what is that mysterious code? It isn't anything you wrote in your program explicitly. You can find out by using the Set Next Statement command in the Disassembly window. Move it to address 00000183, the first executable code after the if() statement. Start stepping, you'll see it executing the constructor of a class named ConsoleApplication1.Program.<>c__DisplayClass5

否则很好地覆盖在现有的SO问题,这是一个自动生成的类拉姆达EX pression在源$ C ​​$ C。这是需要存储在你的程序捕获的变量,列表。既然你跳过它的创作,提领列表中拉姆达总是要轰炸与NRE。

Otherwise well covered in existing SO questions, this is an auto-generated class for the lambda expression in your source code. It is required to store captured variables, list in your program. Since you skipped its creation, dereferencing list in the lambda is always going to bomb with NRE.

漏抽象的标准的情况下,C#有一些,但不是悍然左右。没有什么可以做,当然这件事,你当然可以责怪调试器不能正确地猜测这一点,但它是一个非常难以解决的问题。它不能很容易找到,如果code属于if()语句或code紧随其后。设计问题,调试信息为基础的行号,也没有行code。此外,在总体上与x64的抖动问题,摸索着,即使在简单的情况下。其中应固定在VS2015。

A standard case of a "leaky abstraction", C# has some of it but not outrageously so. Nothing much you can do about it of course, you can certainly blame the debugger for not guessing at this correctly but it is a very difficult problem to solve. It cannot easily find out if that code belongs to the if() statement or the code that follows it. A design issue, debug info is line number based and there is no line of code. Also in general a problem with the x64 jitter, it fumbles even in simple cases. Which should be fixed in VS2015.

这是你必须学会​​坚硬方式™。如果是真的,真的很重要的话,我向您展示了如何正确设置下一条语句,你必须这样做,在反汇编视图,使其工作。随时报告此问题在connect.microsoft.com,我会感到惊讶,如果他们还不知道这件事不过。

This is something you have to learn the Hard Way™. If it is really, really important then I showed you how to set the next statement properly, you have to do it in the Disassembly view to make it work. Feel free to report this issue at connect.microsoft.com, I'd be surprised if they didn't already know about it however.

这篇关于迫使“设置下一条语句”CLR System.NullReferenceException时到'如果'块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆