简体   繁体   中英

Why the following C# code throws NullReferenceException on nullable DateTime initialization?

Here's some simple code:

    static void Main(string[] args)
    {
        var coll = new List<string> {"test", "test2", "test3"};

        var filteredColl = coll.Select(x => x).ToList();

        if (!filteredColl.Any())
        {
            DateTime? date = new DateTime();

            filteredColl = coll.Where(x => date.GetValueOrDefault().Date.ToString(CultureInfo.InvariantCulture) == x).Select(x => x).ToList();
        }
    }

The question is, why the following steps make it crash with NullReferenceException :

1) Breakpoint to the if

断点

2) Set the next execution point:

执行点

3) Try to continue with F10:

例外

If I comment out the last line of the code, it won't crash.

Update: Here's the stack trace:

System.NullReferenceException was unhandled   HResult=-2147467261  
Message=Object reference not set to an instance of an object.  
Source=ConsoleApplication28   StackTrace:
       at ConsoleApplication28.Program.Main(String[] args) in Program.cs: line 21
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()   InnerException:

This is a side-effect of moving the execution point inside a context that declares a captured variable scope. It would be reasonable to report it as an IDE bug, but it would not be trivial to fix. Basically, date is not a variable - it is a field on a capture-context, because of the lambda. The compiler essentially does:

if (!filteredColl.Any())
{
    var ctx = new SomeCaptureContext(); // <== invented by the compiler
    ctx.date = new DateTime();

    filteredColl = coll.Where(ctx.SomePredicate).Select(x => x).ToList();
}

where SomePredicate is:

class SomeCaptureContext {
    public DateTime? date; // yes, a public field - needs to support 'ref' etc
    public bool SomePredicate(string x) // the actual name is horrible
    {
        return this.date.GetValueOrDefault()
              .Date.ToString(CultureInfo.InvariantCulture) == x;
    }
}

The problem here is that when you drag the execution position to:

DateTime? date = new DateTime();

You are actually (in IL terms) dragging it to the line:

ctx.date = new DateTime();

The capture-context line immediately before that, ie

var ctx = new SomeCaptureContext();

never got executed, so ctx is null . Hence the NullReferenceException .

It would be reasonable to log this as a bug, but it is a subtle one - and you wouldn't necessarily always want dragging the execution context to initialize the capture-contexts - it would have to be "if they are null ".

Hmm... Really it looks weird. Do you have mscorlib in references and System in usings? Maybe you have some another class named DateTime and it overrides System.DateTime.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM