Null reference exceptions in .net
we're having this big problem with our application. It's a rather large application with several modules, and thousands and thousands lines of code. A lot of parts of the application are designed to exist only with a reference to another object, for example a Person object can never exists without a House object, so if you at any point in the app say:
bool check = App.Person.House == null;
check
should always be false
(by design), so, to keep using that example, while creating modules, testing, debugging, App.Person.House is never null, but once we shipped the application to our client, they started getting a bunch of NullReferenceException
with the objects that by design, should never have a null reference. They tell us the bug, we try to reproduce it here, but 90% of the times we can't, because here it works fine.
The app is being developed with C# and WPF, and by design, it only runs on Windows XP SP 3, and the .net framework v3.5, so we KNOW the user has the same operative system, service pack, and .net framework version as we do here, but they still get this weird NullReferenceExceptions
that we can't reproduce.
So, I'm just wondering if anyone has seen this before and how you fixed it, we have the app running here at least 8 hours a day in 5 different computers, and we never see those exceptions, this only happens to the client for some reason.
ANY thought, any clue, any solution that could开发者_如何学运维 get us closer to fixing this problem will be greatly appreciated.
Thanks!
Well, you haven't really told us much about the House property... is it writable? If so, put validation into the setter which at least logs if the value is null - and ideally throw an ArgumentNullException
immediately (I believe it's generally better to stop when your data is corrupt rather than continuing and hoping life will sort itself out). If it's just set to a backing field in a constructor, check it there - and again, throw ArgumentNullException
if it's null.
If it's computed in some way, that makes it harder - at that point you should probably test in the getter and log (in some way that will be easy for your customers to get the information back to you) as much information as you think might be relevant.
EDIT: As has been pointed out, this could apply at any level within the expression - so you may well want to apply the same sort of validation and logging to each level.
The line
bool check = App.Person.House == null;
will throw a null-ref exception when either App
or App.Person
is null.
You say 'cannot be null by design' but that is impossible to verify from your description. It seems clear though that you user(s) simply follow a path through your program that is not covered in your test-cases.
If you cannot spot the flaw in your design then the practical approach would be to extend your app with internal checking. I would recommend a good tracing system and (a lot of) System.Diagnostics.Trace.Assert(xx != null);
How do you instantiate your objects? Could it be that the Person object is null? Or even the App object? That would explain the exceptions.
If you can't find the problem, I would suggest you add logging to the application and check if the code that instantiates your objects and the code that uses it to see if you somehow access the objects before they are created.
Another possible problem may be exception handling. Make sure you don't have try-catch blocks that just swallow the exception. These can be the source of some real headaches.
Anyway. These are just suggestions. Without knowing more about your system it is hard to make a better guess.
One very tentative guess is that you have coded yr without a consistent exception handling pattern, and some other exception is being thrown and inappropriately swallowed, which allows the thread of execution to reach a point where code is running that assumes the object has been instantiated, when in fact the previously swallowed or ignored exception has somwhow prevented it from being instantiated, and caused it to still be null.
Guessing why this happen is really hard without understanding the code. Typical sources are
- access unitialized values (left to default null), but usually the C# compiler warns you
- casts (object as Type) when object is an unexpected type, these are usually quickly find in early testing, unless you have a completely untested code path
- allocation failures. In CLR though they raise OutOfMemoryException
- database reads. These are most likely culprit for primitive values, but not for complex classes. ORM layer though may mask the errors and simply assign nulls to members, triggering the errors you see much later
- logic errors. Another likely culprit , the way ur client uses the app exercises a different code path that simply doesn't assign the value
When I was faced with similar problems, I ended up adding instrumentation to the application. Primarily, logging (log4net in my case) and exception reporting. Initially I used Assert, but the CLR asserts are a disaster in production as they pop up the big interrupt message box that requires user interaction and are completely unsuitable for services and background running apps. So I prefer to catch exception, create a report from the exception context, and send the report back to the mothership for analysis. Eventually I was able to track down all issues from those exception reports. I ended up building a service around this concept at http://bugcollect.com and integrate this with log4net, so I could get the reports in real time from all the deployments of my application (from all the clients).
精彩评论