调试和使用调用时出现NullReferenceException

时间:2013-04-17 12:37:05

标签: c# winforms thread-safety nullreferenceexception invoke

昨天我在调试Windows窗体应用程序时发现了一个有趣的行为,请参考以下代码:

bool enter = false;

Debugger.Break(); 
if (enter) // Force to enter the if clause, read next comment
{
    bool a = false; // Bypass previous IF check in debug using 'Set Next Statment (CTRL-SHIFT-F10)' here
                    // Will throw null reference exception

    // If I don't use invoke everything works fine
    Invoke(new MethodInvoker(() =>
    {
        a = true;
    }));
}

因此,如果我强制输入一个不应该在方法上下文中输入的IF子句, AND 代码有一个Invoke委托,它使用它将抛出的IF子句中的任何对象空引用异常。

异常StackTrace:

   at WindowsFormsApplication2.Form1.Test() in c:\WindowsFormsApplication2\Form1.cs:line 26
   at WindowsFormsApplication2.Form1.Form1_Load(Object sender, EventArgs e) in c:\WindowsFormsApplication2\Form1.cs:line 16
   at System.Windows.Forms.Form.OnLoad(EventArgs e)
   at System.Windows.Forms.Form.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WmShowWindow(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

看起来这个对象甚至没有在方法上下文中创建,但它只在我有Invoke时发生,否则它会起作用。

有谁知道异常的根本原因是什么以及为什么它与甚至尚未调用的Invoke方法有关?

1 个答案:

答案 0 :(得分:1)

我理解的匿名方法存放在临时课程中。查看IL时,在进入方法的父范围时会调用临时类的ctor。

根据this article,当您使用周围的局部变量编译匿名方法时会发生以下情况:

  1. 创建一个新的私有类,它是定义匿名方法的类的内部类。
  2. 在新类中创建一个公共数据成员,其类型和名称与匿名方法体中使用的局部变量相同。
  3. 在新类中创建一个包装匿名方法的公共实例方法。
  4. 用新类的声明替换局部变量的声明。创建这个新类的实例 取代当地变量的声明。
  5. 使用数据成员替换匿名方法体内和匿名方法外部的局部变量的使用 新类实例。
  6. 将匿名方法定义替换为新类中定义的实例方法的地址。
  7. a被声明为匿名方法的临时类中的一个字段。在输入父作用域(在本例中为if语句)时调用构造函数,因此通过设置下一个语句,已跳过临时类的构造函数。

    由于临时类现在为null,而a是临时类中的一个字段,因此任何涉及a的内容都会导致NullReferenceException

    .method private hidebysig instance void  button15_Click(object sender,
                                                            class [mscorlib]System.EventArgs e) cil managed
    {
      // Code size       52 (0x34)
      .maxstack  4
      .locals init ([0] bool enter,
               [1] class WindowsFormsApplication1.Form1/'<>c__DisplayClass33' 'CS$<>8__locals34',
               [2] bool CS$4$0000)
      IL_0000:  nop
      IL_0001:  ldc.i4.0
      IL_0002:  stloc.0
      IL_0003:  call       void [mscorlib]System.Diagnostics.Debugger::Break()
      IL_0008:  nop
      IL_0009:  ldloc.0
      IL_000a:  ldc.i4.0
      IL_000b:  ceq
      IL_000d:  stloc.2
      IL_000e:  ldloc.2
      IL_000f:  brtrue.s   IL_0033
      IL_0011:  newobj     instance void WindowsFormsApplication1.Form1/'<>c__DisplayClass33'::.ctor()
      IL_0016:  stloc.1
      IL_0017:  nop
      IL_0018:  ldloc.1
      IL_0019:  ldc.i4.0
      IL_001a:  stfld      bool WindowsFormsApplication1.Form1/'<>c__DisplayClass33'::a
      IL_001f:  ldarg.0
      IL_0020:  ldloc.1
      IL_0021:  ldftn      instance void WindowsFormsApplication1.Form1/'<>c__DisplayClass33'::'<button15_Click>b__32'()
      IL_0027:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.MethodInvoker::.ctor(object,
                                                                                                         native int)
      IL_002c:  call       instance object [System.Windows.Forms]System.Windows.Forms.Control::Invoke(class [mscorlib]System.Delegate)
      IL_0031:  pop
      IL_0032:  nop
      IL_0033:  ret
    } // end of method Form1::button15_Click