views:

658

answers:

11

Apparently the vast majority of errors in code are null reference exceptions. Are there any general techniques to avoid encountering null reference errors?

Unless I am mistaken, I am aware that in languages such as F# is it not possible to have a null value. But thats not the question, I'm asking how to avoid null reference errors in languages such as C#.

+3  A: 

One way is to use the Null Value Objects (aka the Null Object Pattern) where possible. There are more details here

Preet Sangha
+2  A: 

Really if in your language there are null values, it's bound to happen. The null reference errors come from errors in application logic - so unless you can avoid all of those your're bound to hit some.

Jeremy Raymond
+2  A: 

Appropriate use of structured exception handling can help avoid such errors.

Also, unit testing can help you make sure your code behaves as expected, including ensuring that values are not null when they're not supposed to be.

Andy West
+4  A: 

Using Null Object Patterns is key here.

Make sure that you require collections to be empty in the case when they're not populated, rather than null. Using a null collection when an empty collection would do is confusing and often unnecessary.

Finally, I make my objects assert for non-null values upon construction wherever possible. That way I'm in no doubt later on as to whether values are null, and only have to perform null checks where essential. For most of my fields and parameters I can assume that values are not null based on previous assertions.

Brian Agnew
+2  A: 

One of the most common null reference errors that I've seen is from strings. There will be a check:

if(stringValue == "") {}

But, the string is really null. It should be:

if(string.IsNullOrEmpty(stringValue){}

Also, you could be overly cautious and check an object isn't null before you attempt to access members/methods of that object.

Jim Schubert
+4  A: 

You can easily check for a null reference before it causes an exception, but usually that is not the real problem, so you would just end up throwing an exception anyway as the code can't really continue without any data.

Often the main problem isn't the fact that you have a null reference, but that you got a null reference in the first place. If a reference is not supposed to be null, you shouldn't get past the point where the reference is initialised without having a proper reference.

Guffa
+1 Simple and easy
ChadNC
+4  A: 

You don't.

Or rather, there's nothing special to do to try to 'prevent' NREs in C#. For the most part an NRE is just some type of logic error. You can firewall these off at interface boundaries by checking parameters and having lots of code like

void Foo(Something x) {
    if (x==null)
        throw new ArgumentNullException("x");
    ...
}

all over the place (much of the .Net Framework does this), so that when you do screw up, you get a slightly more informative diagnostic (the stack trace is even more valuable, though, and an NRE provides that too). But you still just end up with an exception.

(Aside: Exceptions like these - NullReferenceException, ArgumentNullException, ArgumentException, ... - typically should not be caught by the program, but rather just means "developer of this code, there is a bug, please fix it". I refer to these as a 'design time' exceptions; contrast these with true 'run time' exceptions that happen as a result of the run time environment (e.g. FileNotFound) and are intended to potentially be caught and handled by the program.)

But at the end of the day, you just have to code it right.

Ideally the majority of NREs would never happen because 'null' is a nonsensical value for many types/variables, and ideally the static type system would disallow 'null' as a value for those particular types/variables. Then the compiler would prevent you from introducing this type of accidental error (ruling out certain classes of errors are what compilers and type systems are best at). This is where certain languages and type systems excel.

But without those features, you just test your code to ensure you don't have code paths with this type of error (or possibly use some outside tools that can do extra analysis for you).

Brian
+1 for "design time" exceptions -- interesting distinction. I've had arguments with people at work about exceptions where I've said the same thing. The person says "exceptions are too slow", then I say "but if we code it right then we won't trigger or handle any of these exceptions anyway!"
Mark Simpson
+5  A: 

In addition to the above (Null Objects, Empty Collections), there are some general techniques, namely Resource Acquisition is Initialization (RAII) from C++ and Design By Contract from Eiffel. These boil down to:

  1. Initialize variables with valid values.
  2. If a variable can be null, then either check for null and treat it as a special case or expect a null reference exception (and deal with that). Assertions can be used to test for contract violations in development builds.

I've seen a lot of code that looks like this:

if ((value != null) && (value.getProperty() != null) && ... && (...doSomethingUseful())

A lot of the time this is completely unnecessary and most of the tests could be removed with stricter initialization and tighter contract definitions.

If this is a problem in your code base then it is necessary to understand in each case what the null represents:

  1. If the null represents an empty collection, use an empty collection.
  2. If the null represents an exceptional case, throw an Exception.
  3. If the null represents an accidentally uninitialized value, explicitly initialize it.
  4. If the null represents a legitimate value, test for it - or even better use a NullObject that performs a null op.

In practice this standard of clarity at the design level is non-trivial and requires effort and self-discipline to apply consistently to your code base.

richj
+1  A: 

One of the simplest ways to avoid NullReferenceExceptions is to aggressively check for null references in your class constructors/methods/property setters and draw attention to the problem.

E.g.

public MyClass
{
   private ISomeDependency m_dependencyThatWillBeUsedMuchLater 

   // passing a null ref here will cause 
   // an exception with a meaningful stack trace    
   public MyClass(ISomeDependency dependency)
   {
      if(dependency == null) throw new ArgumentNullException("dependency");

      m_dependencyThatWillBeUsedMuchLater = dependency;
   }

   // Used later by some other code, resulting in a NullRef
   public ISomeDependency Dep { get; private set; }
}

In the above code, if you pass a null ref, you will find out immediately that the calling code is using the type incorrectly. If there was no null reference check, the error can be obscured in many different ways.

You'll notice that the .NET framework libraries nearly always fail early and often if you provide null references where it's invalid to do so. Since the exception thrown explicitly says "you messed up!" and tells you why, it makes detecting and correcting defective code a trivial task.

I've heard complaints from some developers who say this practice is overly verbose and redundant as a NullReferenceException is all you need, but in practice I find it makes a big difference. This is especially the case if the call stack is deep and/or the parameter is stored and its use is deferred until later (perhaps on a different thread or obscured in some other way).

What would you rather have, an ArgumentNullException at the entry method, or an obscure error in the guts of it? The further you move away from the source of an error, the harder it is to trace it.

Mark Simpson
A: 

Good code analysis tools can help here. Good unit tests can also help if you're using tools that consider null as a possible path through your code. Try throwing that switch in your build settings that says "treat warnings as errors" and see if you can keep the # of warnings in your project = 0. You may find the warnings are telling you a lot.

One thing to keep in mind is that it may be a good thing that you are throwing a null - reference exception. Why? because it may mean that code that should have executed did not. Initializing to default values is a good idea, but you should be careful that you don't end up hiding a problem.

List<Client> GetAllClients()
{
    List<Client> returnList = new List<Client>;
    /* insert code to go to data base and get some data reader named rdr */
   for (rdr.Read()
   {
      /* code to build Client objects and add to list */
   }

   return returnList;
}

Alright, so this may look ok, but depending on your business rules, this may be a problem. Sure, you'll never throw a null reference, but maybe your User table should never be empty? Do you want your app to be spinning in place, generating support calls from users saying "it's just a blank screen", or do you want to raise an exception that might get logged somewhere and raise an alert quickly? Don't forget to validate what you're doing as well as 'handling' exceptions. This is one of the reasons why some are loathe to take nulls out of our languages... it makes it easier to find the bugs even though it may cause some new ones.

Remember: Handle exceptions, don't hide them.

Jim Leonardo
+3  A: 

When a null reference exception is displayed to the user, this indicates a defect in the code resulting from an error on the part of the developer. Here are some ideas on how to prevent these errors.

My top recommendation for people who care about software quality, and are also using the.net programming platform, is to install and use Microsoft code contracts ( http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx ). It includes capabilities to do run-time checking and static verification. The essential capability to build these contracts into your code is being included in the 4.0 version of the.net framework. If you are interested in code quality, and it sounds like you are, you may really enjoy using Microsoft code contracts.

With Microsoft code contracts, you can guard your method from null values by adding preconditions like this "Contract.Requires(customer != null);". Adding a precondition like this is equivalent to the practice recommended by many others in their comments above. Prior to code contracts, I would have recommended you do something like this

if (customer == null) {throw new ArgumentNullException("customer");}

Now I recommend

Contract.Requires(customer != null);

You can then enable the run-time checking system which will catch these defects as early as possible, leading you towards diagnosis and correction of the defective code. But don't let me give you the impression that code contracts are simply a fancy way to replace argument null exceptions. They are much more powerful than that. With Microsoft code contracts, you can also run the static checker, and ask it to investigate possible sites in your code where null reference exceptions might occur. The static checker requires a bit more experience to use easily. I would not recommend it first for beginners. But feel free to try it out and see for yourself.

RESEARCH ON THE PREVALENCE OF NULL REFERENCE ERRORS

There has been some debate in this thread on whether null reference errors are a significant problem. A long-winded answer is below. For people who don't want to wade through that, I will summarize.

  • Microsoft's leading researchers in program correctness on the Spec# and code contracts projects believe it is a problem worth addressing.
  • Dr. Bertrand Meyer and the team of software engineers at ISE, who developed and support the Eiffel programming language, also believe it is a problem worth addressing.
  • In my own commercial experience developing ordinary software, I have seen null reference errors often enough, that I would like to address the problem in my own products and practices.

For years, Microsoft has invested in research designed to improve software quality. One of their efforts was the Spec# project. One of the most exciting developments in my opinion with the.net 4.0 framework, is the introduction of Microsoft code contracts, which is an outgrowth of the earlier work done by the Spec# research team.

Regarding your remark "the vast majority of errors in code are null reference exceptions", I believe it is the qualifier "the vast majority" that will cause some disagreements. The phrase "Vast majority" suggests that perhaps 70-90% of faults have a null reference exception as the root cause. This seems far too high to me. I prefer to quote from the research of the Microsoft Spec#. In their article The Spec# programming system: An overview, by Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte. In CASSIS 2004, LNCS vol. 3362, Springer, 2004, they wrote

1.0 Non-Null Types Many errors in modern programs manifest themselves as null-dereference errors, suggesting the importance of a programming language providing the ability to discriminate between expressions that may evaluate to null and those that are sure not to (for some experimental evidence, see [24, 22]). In fact, we would like to eradicate all null dereference errors.

This is a likely source for people at Microsoft who are familiar with this research. This article is available at the Spec# site.

I've copied references 22 and 24 below, and included the ISBN for your convenience.

  • Manuel Fahndrich and K. Rustan M. Leino. Declaring and checking non-null types in an object-oriented language. In Proceedings of the 2003 ACM Conference on Object-Oriented Programming, Systems, Languages, and Applications, OOPSLA 2003, volume 38, number 11 in SIGPLAN Notices, pages 302–312. ACM, November 2003. isbn = {1-58113-712-5},

  • Cormac Flanagan, K. Rustan M. Leino, Mark Lillibridge, Greg Nelson, James B. Saxe, and Raymie Stata. Extended static checking for Java. In Proceedings of the 2002 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), volume 37, number 5 in SIGPLAN Notices, pages 234–245. ACM, May 2002.

I reviewed these references. The first reference indicates some experiments they did reviewing their own code for possible null reference defects. Not only did they find several, but in many cases, the identification of a potential null reference indicated a broader problem with the design.

The second reference does not provide any specific evidence for the assertion that null reference errors are problem. But the authors do state that in their experience, these null reference errors are significant source of software defects. The paper then proceeds to explain how they try to eradicate these defects.

I also remembered seeing something about this in an announcement from ISE on a recent release of Eiffel. They refer to this issue as "void safety", and like so many things inspired or developed by Dr. Bertrand Meyer, they have an eloquent and educational description of the problem and how they go about preventing it in their language and tools. I recommend you read their article http: SLASH SLASH doc.eiffel.com/book/method/void-safety-background-definition-and-tools to learn more.

If you want to learn more about Microsoft code contracts, there are tons of articles that have arisen recently. You can also check my blog at http: SLASH SLASH codecontracts.info which is primarily devoted to conversations about software quality through the use of programming with contracts.

Sorry for the weird links above, but spam prevention strategies on this site prevent me from posting more than one link.

David Allen