Can Visual Studio tell me which reference threw a

2019-01-24 07:17发布

I'm writing unit tests for an MVC web app, and I've been getting null reference exceptions because the mocked-up test objects are only partly initialized. I know which line is throwing the exceptions, and it looks something like this:

return Supervisor.RegistrationInformation.Registrations
    .Any(r =>
        r.RegistrationCountry.IsUSAOrCandada() &&
        (!DatesWorked.Start.HasValue || r.RegistrationDate <= DatesWorked.Start.Value) &&
        (!DatesWorked.End.HasValue || r.RegistrationExpirationDate >= DatesWorked.End.Value) &&
        //...

There are a lot of references in there, and any of them could be the problem. However, NullReferenceException itself doesn't seem to capture which reference blew up. The fact that I'm passing in a lambda presents another challenge: As far as I can tell, I can't step through the lambda during debugging and see which members of r are null.

Is there any way I can do one or both of the following:

  • Have Visual Studio tell me exactly which reference threw the NullReferenceException?
  • Failing that, is there a way to make the debugger step through the lambda expression (or just hover over things to see their values) as it's being evaluated by Any?

I feel like there must be a way to do these things, but I can't seem to find it. I'm on VS2010 Premium, and I have Resharper, VS Power Tools, and a couple other extensions installed. If there's an add-on that does this, I'd be fine with that.

Edit:

As Eric Lippert points out, it's impossible to pinpoint the source of an NR exception when the code has been compiled in Release configuration. I'm only asking about working in debug mode. If Visual Studio (or some extension to VS) can track the source of a reference while debugging, that would answer my question.

Edit 2:

The second question--how to break and step through a lambda--has been answered, but I'd still like to know if there's an automatic way to track down a null reference.

8条回答
forever°为你锁心
2楼-- · 2019-01-24 08:04

The immediate problem is that the lambda is wrapping a lot of complex logic up in a single statement, so you can't find where the crash happened.

But that's just a side effect. The real problem is that your code is assuming, incorrectly, that none of the references will be null.

One approach would be to try to isloate the crash and put a bandage over the "bit that broke". But that will not attack the root of the problem: there are unchecked assumptions in the code, and you already have proof that at least one of them is wrong. If another one is wrong, then at some undefined point in future, your program will probably crash again, and you will debug and bandage again. This can go on and on, and your code will get hacked about every time.

You need to put down your debugger and think about the code. All of the code, in one pass. "Desk-check" it: run through each part of the expression and ask yourself "Can this bit be null? What will happen if it is? And if so, how can I make it safe?"

That way, you will be able to re-write the entire expression in a form that you know is null-aware, and you won't ever need to debug into it to work out why it blew up.

For example, this:

    r.RegistrationCountry.IsUSAOrCandada() && 

...could cause a null dereference if r==null or if r.RegistrationCountry==null. The code needs to check for these possibilities. The "most defensive" code would be to check every reference something like this:

    r != null && r.RegistrationCountry != null && r.RegistrationCountry.IsUSAOrCandada() && 

which guarantees that each step will only be executed if the previous step succeeded. Note though, that the list may never provide r==null, so that check might not be necessary. Or r.RegistrationCountry may be a struct (a non-nullable type), so you'll know that check is unrequired. So you can avoid unnecessary checks by thinking about it. But you need to think through each part of the code to challenge and eliminate all the assumptions.

查看更多
我只想做你的唯一
3楼-- · 2019-01-24 08:07

You can put a breakpoint inside the lambda expression, and when it hits, you should be able to hover over the expression and see their values just fine.

Looking at your code, I can see that only one of three expressions might've caused the NullRef- r, r.RegistrationCountry and DatesWorked.

Put those three expressions in your Watch window, and either ask the debugger to break on any NullReferenceException (via Debug->Exceptions), or put a breakpoint inside the lambda expression, and make it a conditional breakpoint on the condition r == null || r.RegistrationCountry == null || DatesWorked == null, and the answer should appear pretty quick.

查看更多
登录 后发表回答