Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Let's say I have a method:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

I've been trained to believe that throwing the ArgumentNullException is "correct" but an "Object reference not set to an instance of an object" error means I have a bug.

Why?

I know that if I was caching the reference to someObject and using it later, then it's better to check for nullity when passed in, and fail early. However, if I'm dereferencing it on the next line, why are we supposed to do the check? It's going to throw an exception one way or the other.

Edit:

It just occurred to me... does the fear of the dereferenced null come from a language like C++ that doesn't check for you (i.e. it just tries to execute some method at memory location zero + method offset)?

share|improve this question
    
Making the exception explicit helps affirm that there is a possibility of error, which can be directly noted in documentation. –  zzzzBov Nov 22 '11 at 20:52

7 Answers 7

up vote 21 down vote accepted

I suppose if you are immediately dereferencing the variable, you could debate either way, but I would still prefer the ArgumentNullException.
It is much more explicit about what is going on. The exception contains the name of the variable that was null, whereas a NullReferenceException does not. Particularly at the API level, an ArgumentNullException makes it clear that some caller did not honor the contract of a method, and the failure was not necessarily a random error or some deeper issue (though that may still be the case). You should fail early.

In addition, what are later maintainers more likely to do? Add the ArgumentNullException if they add code before the first dereference, or simply add the code and later on allow a NullReferenceException to be thrown?

share|improve this answer
    
By extension, if this were a non-public method, or a public method on a non-public class, you don't think there's a good reason for the null check? –  Scott Whitlock Nov 22 '11 at 17:45
    
I usually don't add these checks to private methods, but I still may add them to public methods of private classes to be consistent. For private methods, I do tend to make an assumption that arguments are validated earlier at some public-call level. –  Matt H Nov 22 '11 at 17:48

I've been trained to believe that throwing the ArgumentNullException is "correct" but an "Object reference not set to an instance of an object" error means I have a bug. Why?

Suppose I call method M(x) that you wrote. I pass null. I get an ArgumentNullException with the name set to "x". That exception unambiguously means that I have a bug; I should not have passed null for x.

Suppose I call a method M that you wrote. I pass null. I get a null deref exception. How the heck am I supposed to know whether I have a bug or you have a bug or some third party library that you called on my behalf has a bug? I know nothing about whether I need to fix my code or call you up to tell you to fix yours.

Both exceptions are indicative of bugs; neither should ever be thrown in production code. The question is which exception communicates who has the bug better to the person doing the debugging? Null deref exception tells you almost nothing; argument null exception tells you a lot. Be kind to your users; throw exceptions that enable them to learn from their mistakes.

share|improve this answer

I believe it's only advantage is that you can provide better diagnostics in the exception. That is you know, that the caller of the function is passing null, while if you let the dereference throw it, you would not be sure whether the caller passes wrong data or there is a bug in the function itself.

I would do it if somebody else is going to call the function to make it easier to use (debug if something goes wrong) and not do it in my internal code, because the runtime will check it anyway.

share|improve this answer

The point is, your code should do something meaningful.

A NullReferenceException is not particularly meaningful, because it could mean about everything. Without looking at where the exception was thrown (and the code might not be available to me) I cannot figure out, what went wrong.

An ArgumentNullException is meaningful, because it tells me: the operation failed because the argument someObject was null. I don't need to look at your code to figure that out.

Also raising this exception means, that you explicitly handle null, even though your only way to do it is to say, that you can't handle it, while just letting the code crash could potentially mean that, you might actually be able to handle null, but forgot to implement the case.

The point of exceptions is to communicate information in case of failure, which can be handled at runtime to recover from failure, or at debug time to better understand what went wrong.

share|improve this answer

I've been trained to believe that throwing the ArgumentNullException is "correct" but an "Object reference not set to an instance of an object" error means I have a bug.

You have a bug if the code does not work as designed. An NullReferenceException is thrown by the system and that fact really makes more of a bug than a feature. Not only because you did not define the specific exception to be handled. Also because a developer reading your code may assume you did not account for the situation to occur.

For similar reasons that the ApplicationException belongs to your application vs a general Exception which belongs to the operating system. The type of exception being thrown indicates where the exception originates.

If you have accounted for null, it's better that you handle it gracefully by throwing a specific exception or if it's an acceptable input, then do not throw an exception because they are expensive. Handle it by performing operations with good defaults or no operation at all (e.g. no update required).

Finally, don't neglect the caller's ability to handle the situation. By giving them a more specific exception, ArgumentException they can construct their try/catch in such a way to handle this error before the more general NullReferenceException which may be caused by any number of other reasons that occur elsewhere, like a failure to initialize a constructor variable.

share|improve this answer

The check makes it more explicit what's going on, but the real problem comes with code like this (contrived) example:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

Here there's a call chain involved and with no null check you only see the error in someOtherObject and not where the problem first revealed itself - which instantly means time wasted debugging or interpreting call stacks.

In general it's better to be consistent with this sort of thing and always add the null check - which makes it easier to spot if one is missing rather than picking and choosing on a case-by-case basis (assuming that you know that null is always invalid).

share|improve this answer

Another reason to consider is what if some processing occurs before the null object is used?

Say you have a data file of associated company IDs (Cid) and person IDs (Pid). It's saved as a stream of number pairs:

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

And this VB function write the records to the file:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

If Person is a null reference, the line File.Write(Person.ID) will throw an exception. The software can catch the exception and recover, but this leaves a problem. A company ID has been written without an associated person ID. If fact, when you next call this function, your going to put a company ID where the previous person ID was expected. As well as that record being incorrect, every subsequent record will have a person ID followed by a company ID, which is the wrong way around.

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

By validating the parameters at the start of the function, you prevent any processing from occuring when the method is guaranteed to fail.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.