CLR System.NullReferenceException when forcing 'Set Next Statement' into 'if' block

后端 未结 1 506
慢半拍i
慢半拍i 2020-12-09 10:21

Background

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.

相关标签:
1条回答
  • 2020-12-09 10:57

    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...
    

    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

    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.

    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.

    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.

    0 讨论(0)
提交回复
热议问题