79

What was the design decision behind NullReferenceException not containing any runtime specific information except base class data (like a stacktrace)? And is there an extension for Visual Studio that can tell you straight away which part of an expression was null?

Egor Pavlikhin
  • 17,503
  • 16
  • 61
  • 99
  • 2
    If I remember well, ReSharper can tell you if an expression can throw such an exception. – Etienne de Martel Jan 17 '11 at 23:36
  • 4
    Reshaper can predict that something _may_ throw such an exception. – jcolebrand Jan 17 '11 at 23:41
  • You are both right. But ReSharper, even if is a very good helper, has both false positives and false negatives. Often I had to manually disable the notice by comment, because I was mathematically sure about the impossibility of such an exception. – usr-local-ΕΨΗΕΛΩΝ Jan 17 '11 at 23:52
  • @djechelon ~ Hence the reason I counter-qualified his statement. It can't know _that_ it will happen, it can only know that it _may_ happen (your comment) – jcolebrand Jan 17 '11 at 23:53
  • @drachenstern: don't worry, I upvoted your comment, and I just wanted to expand it with mine, highlighting the fact that it's possible to also have fake negatives (you can predict it *may*, but can you predict that a statement **won't** throw an exception?). Simply that ;) – usr-local-ΕΨΗΕΛΩΝ Jan 17 '11 at 23:59
  • @djechelon ~ Ahhh, that makes sense. I like the clarification, because to me the _may_ implies _probably won't be right_ ;) ... (we're on the same page now) – jcolebrand Jan 18 '11 at 00:32

6 Answers6

83

An NRE is a very low-level exception. It is a hardware exception (a 'trap') generated by the processor when it is asked to read data from an address below 64K. That region of the virtual memory space is always unmapped, specifically to trap pointer bugs. It starts as an AccessViolation and gets turned into NRE by the CLR when the address is less than 0x00010000. At that point there is very little context for the exception, all that's known is the address of the machine code instruction that caused the trap.

Reverse-engineering that machine code instruction address back to a named variable in your program isn't possible. It is important that it works that way, a jitter would have to generate very inefficient code otherwise. All that can be reasonably done is recover the source code line number. That requires debugging info (a .pdb) that contains line number info. The CLR knows how to read the .pdb file and uses it to generate the exception's stack trace. However, this is often still inaccurate due to optimizations performed by the JIT optimizer, it moves code around. You'll only get a guaranteed match for the Debug build. The reason why the PDB for the Release build doesn't contain source line number info. You can change that.

This problem has a very simple solution. Check for null yourself and generate your own exception before letting the runtime do it. The test is quite cheap, well less than a nanosecond.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 3
    Providing comprehensive information in error messages is a worthwhile goal for computer science. I hope that some day either languages or computers are changed to the point where "wrap everything in a guard clause" is not the best possible answer. – Greg Oct 22 '13 at 17:40
  • 15
    I certainly hope not. I'd be out of a good paying job if it was easy. – Hans Passant Oct 22 '13 at 17:46
  • Not trying to imply it was easy. :) – Greg Oct 22 '13 at 17:52
  • 3
    @HansPassant .NET 4.6.2 apparently allows you to see which expression is `null` when an NRE occurs. Any idea how this might be possible after all? https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/ – Jeroen Vannevel Aug 03 '16 at 11:22
  • 1
    I'll figure it out when it lands on my machine. I'm not thrilled about updates lately, too many bugs. – Hans Passant Aug 03 '16 at 11:28
  • 6
    I'm gonna call BS on this. We can get a line number for a NRE, that's not in machine code and too low-level to reverse engineer. The stacktrace generation at this point should be able to at least give you the statement that generated the exception. – Jeremy Holovacs Jan 17 '17 at 14:19
  • Thank you for expanding beyond the usual "NRE means it's null, duh", delving into why it can't provide context of what it was trying to evaluate. – Shazbot Dec 01 '17 at 17:22
  • @JeremyHolovacs, I think I figured it out. [Craig points out](https://stackoverflow.com/a/115732/1739000) that the assembly has the symbol names, but the bytecode doesn't. When debugging, you have the full assembly available to you (including... debugging symbols... funny name coincidence!). It is sad news, but it looks like we are stuck with it (unless MS miraculously decides to revamp their entire framework). – NH. Mar 01 '18 at 23:41
28

You can setup Visual Studio to break on Throw for NullReferenceException immediately and not first at the catch block.

  • Debug
  • Exceptions
  • Common Language
  • Runtime Exceptions System
  • System.NullReferenceException (Check the box)

Then you will have a break in the line which caused the NullReferenceException.

Martin Buberl
  • 45,844
  • 25
  • 100
  • 144
5

There's no trivial way to determine what was null at runtime -- the necessary information would have to be decompiled and inferred from the IL, which is sufficiently low-level that all it knows is that "the item at the top of the stack is null" but not how that item got there.

(And I don't know of such an extension, but it would be a nice debugger enhancement)

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • Well it has to be mid-process on a stack to know that the thing it popped was null, so I don't know how it can't know the stack frame but it can know that the thing at the top of the stack is null... – jcolebrand Jan 17 '11 at 23:40
  • I have to correct you. The IL language obviously doesn't know anything about the object on top of stack, but if you have debug symbols you could have more hints. A statement like one in my answer is complex enough to be splitted in many IL statements: simply bind each call to one of the objects involved and you have your null. I don't know if **current** debug symbols can help with something like that, but I agree (+1) that this would be a great and cheap enhancement!! – usr-local-ΕΨΗΕΛΩΝ Jan 17 '11 at 23:46
4

NullReferenceException is thrown by the runtime, not by the language. The runtime doesn't know where the reference came; it just knows that an instruction tried to use a null reference.

In fact, the exception is "probably" the result of a native windows "invalid access violation" SEH exception, which is caught by the runtime and translated into IL exception ("probably": I have not checked if this is the case, but it's my guess of what's the most performing way to JIT the code).

Euro Micelli
  • 33,285
  • 8
  • 51
  • 70
3

Not all information about your code is available at runtime. For example, local variable names aren't available. So, it is impossible to throw exception with message like 'local variable myObj is null, but it's member has been called'. Moreover there are lot of non-user-written objects at runtime (for example classes, generated for closures/anonymous types/iterators etc.) which also could be a source of null-ref exceptions.

Victor Haydin
  • 3,518
  • 2
  • 26
  • 41
0

If you have debug symbols, you can track down the line that caused the exception. If it's simple enough, you'll be straight getting your null value. Otherwise (think about a.Value = b.Do(c.GetX(),d.GetY(z.ToString()));) you must debug with your IDE

usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305