问题
I found an interesting behavior yesterday while debugging a Windows Forms application, please refer to this code:
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;
}));
}
So if I force to enter an IF clause that was not supposed to be entered in the method context, AND the code has an Invoke delegate that uses any object from within the IF clause it will throw a null reference exception.
Exception 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)
Seems like that the object was not even created in the method context, but it only happens when I have the Invoke, otherwise it will work.
Does anyone knows what is the root cause of the exception and why it is related to a Invoke method that wasn't even called yet?
回答1:
Anonymous methods to my understanding are housed in a temporary class. Looking at the IL the temporary class's ctor gets called when entering the parent scope of the method.
According to this article the following happens when you compile an anonymous method with a surrounding local variable:
- Create a new private class that is an inner class of the class where the anonymous method is defined.
- Create a public data member within the new class, with the same type and name as the local variable that is used in the anonymous method body.
- Create a public instance method within the new class that wraps the anonymous method.
- Replace the declaration of the local variable with the declaration of the new class. Create an instance of this new class in place of the declaration of the local variable.
- Replace the use of the local variable within the anonymous method body and outside the anonymous method, with the data member of the new class instance.
- Replace the anonymous method definition with the address of the instance method defined in the new class.
a
is declared as a field within the anonymous method's temporary class. The constructor is invoked when entering the parent scope (in this case the if statement) so by setting the next statement, the constructor of the temporary class has been skipped.
Since the temporary class is now null, and a
is a field on the temporary class, it makes sense that anything involving a causes 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
来源:https://stackoverflow.com/questions/16060244/nullreferenceexception-when-debugging-and-using-invoke