Using the following code as an example:
if (true)
{
string foo = null;
List<string> bar = new List<string>
{
"test"
};
bar.Any(t => t == foo);
}
If I run this program in a regular way (without a break point or any other interruption), everything works without exception or error (as you would expect it).
Now if I put a break point on the if statement and move the cursor to the curly brace as described in the following picture (using my mouse, not using F10, so skipping the if(true)
statement):
I get an exception of type System.NullReferenceException
when the debugger executes the statement string foo = null
It seems to be linked to the fact that the variable foo
is used in the lambda expression inside the if
statement. I have tested and reproduced this on Visual Studio 2012 and 2013 (pro and ultimate).
Any idea on why this could be happening?
Eric's answer and comments already describe why it can happens in general. I'd like to highlight whats going on in this particular case.
Here is a generated IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<string> bar,
[1] class [mscorlib]System.Collections.Generic.List`1<string> <>g__initLocal0,
[2] class StackOverflow.Program/<>c__DisplayClass2 CS$<>8__locals3,
[3] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.3
L_0003: newobj instance void StackOverflow.Program/<>c__DisplayClass2::.ctor()
L_0008: stloc.2
L_0009: nop
L_000a: ldloc.2
L_000b: ldnull
L_000c: stfld string StackOverflow.Program/<>c__DisplayClass2::foo
L_0011: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
L_0016: stloc.1
L_0017: ldloc.1
L_0018: ldstr "test"
L_001d: callvirt instance void [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
L_0022: nop
L_0023: ldloc.1
L_0024: stloc.0
L_0025: ldloc.0
L_0026: ldloc.2
L_0027: ldftn instance bool StackOverflow.Program/<>c__DisplayClass2::<Main>b__1(string)
L_002d: newobj instance void [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
L_0032: call bool [System.Core]System.Linq.Enumerable::Any<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
L_0037: pop
L_0038: nop
L_0039: ret
}
Note L_0003
line. It calls ctor for auto-generated c__DisplayClass2
class, which hold foo
field, since you use it in a lambda. So NullReferenceException
happens because your skip class initialization, but then you assigning instance's field foo
on a line L_000c
.
Too bad, there is no easy way to debug on IL level to verify this, but we can debug JITed program (Debug -> Disassembly)
Here is your first breakpoint:
And then after cursor move:
One of these skiped call instructures must be call to ctor from L_0003
.