1

我昨天在调试 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 子句,并且代码具有使用 IF 子句中的任何对象的 Invoke 委托,它将引发空引用异常。

异常堆栈跟踪:

   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 方法有关?

4

1 回答 1

3

据我了解,匿名方法位于一个临时类中。查看IL,进入方法的父范围时会调用临时类的ctor。

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

  1. 创建一个新的私有类,它是定义匿名方法的类的内部类。
  2. 在新类中创建一个公共数据成员,其类型和名称与匿名方法主体中使用的局部变量相同。
  3. 在包装匿名方法的新类中创建一个公共实例方法。
  4. 用新类的声明替换局部变量的声明。创建这个新类的实例来代替局部变量的声明。
  5. 将匿名方法体内部和匿名方法外部的局部变量的使用替换为新类实例的数据成员。
  6. 将匿名方法定义替换为新类中定义的实例方法的地址。

a被声明为匿名方法的临时类中的一个字段。进入父作用域时调用构造函数(在本例中为 if 语句),因此通过设置下一条语句,临时类的构造函数已被跳过。

由于临时类现在为 null,并且a是临时类上的一个字段,因此任何涉及 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
于 2013-04-17T12:59:10.270 回答