views:

551

answers:

7

What types of exceptions should be thrown for invalid or unexpected parameters in .NET? When would I choose one instead of another?

Follow-up:

Which exception would you use if you have a function expecting an integer corresponding to a month and you passed in '42'? Would this fall into the "out of range" category even though it's not a collection?

+2  A: 

argument exception.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException
Syed Tayyab Ali
+14  A: 

Depending on the actual value and what exception fits best:

If this is not precise enough, just derive your own exception class from ArgumentException.

Yoooder's answer enlightened me. An input is invalid if it is not valid at any time while an input is unexspected if it is not valid for the current state of the system. So in the later case an InvalidOperationException is a reasonable choice.

Daniel Brückner
Taken from MSDN's page on InvalidOperationException: "InvalidOperationException is used in cases when the failure to invoke a method is caused by reasons other than invalid arguments."
STW
+24  A: 

I like to use: ArgumentException, ArgumentNullException, ArgumentOutOfRangeException

Edit

You could also throw as Yoooder points out bellow (vote his answer up as well), that InvalidOperationException makes sense if your given a valid argument but the method being called is not valid for the argument based on the current state.

You could also throw the NotSupportedException if the arguments passed in are just not supported. Imagine an Ftp client, and you pass a command in that the client doesn't support. This would be a valid usage as well.

The trick is to throw the exception that best expresses why the method cannot be called in the current state.

Edit

Thanks for the link I updated all to point to msdn.

Edit

In response to your followup, I would throw an ArgumentOutOfRangeException, look at what MSDN says about this exception:

ArgumentOutOfRangeException is thrown when a method is invoked and at least one of the arguments passed to the method is not nullNothingnullptra null reference (Nothing in Visual Basic) and does not contain a valid value.

So in this case you are passing a value but that is not a valid value, since your range is 1-12. However, way you go document it make it clear what your API throws. Because although I might say ArgumentOutOfRangeException another developer might say ArgumentException. Make it easy and document the behavior.

Edit

As Yoooder again points out in the comments documentation is good but to a point. Ideally the exception should be detailed about what went wrong, why it is wrong, and how to fix it. I love when error messages point to help documentation or other resources.

For example Microsoft did a good first step with this. When you encounter the error they tell you in the error messgae the KB article. What they don't do well is tell why you it specifically failed.

JoshBerke
Reference to MSDN page stating these conventions: http://msdn.microsoft.com/en-us/library/system.argumentexception(loband).aspx (in Remarks section)
Tormod Fjeldskår
Psst! I agree that you answered his specific question exactly right, but see my answer below to help round out defensive coding and validating parameters ;-D
STW
+1 but documenting what exception is thrown and why is more important than picking the 'right' one.
pipTheGeek
@pipTheGeek - I think it's really a moot point. While documenting is definately important it also expects the consuming developer to be proactive or defensive and to actually read the documentation in detail. I would opt for a friendly/descriptive error over good documentation since the end-user has the chance of seeing one of them and not the other; there's a better chance of having an end-user communicate a descriptive error to a poor programmer than a poor programmer reading the full docs
STW
:( there's no flag for misspelling my precious screen name :(
STW
Note, if you catch ArgumentException, it will also catch ArgumentOutOfRange.
SnOrfus
@Yoooder: I agree with you on that, I'd love the best of both worlds however, give me a good descriptive error message that points me to the documentation on the error;-)
JoshBerke
+1  A: 

ArgumentException:

ArgumentException is thrown when a method is invoked and at least one of the passed arguments does not meet the parameter specification of the called method. All instances of ArgumentException should carry a meaningful error message describing the invalid argument, as well as the expected range of values for the argument.

A few subclasses also exist for specific types of invalidity. The link has summaries of the subtypes and when they should apply.

Ben S
A: 

There is a standard ArgumentException that you could use, or you could subclass and make your own. There are several specific ArgumentException classes:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Whichever one works best.

Scott M.
I disagree for nearly all cases; the provided .NET Argument*Exception classes are very commonly used and provide you the ability to provide enough specific information to notify the consumer of the issue.
STW
To clarify - I disagree for nearly all cases of deriving from the Argument*Exception classes. Using one of the .NET Argument Exceptions plus a descriptive and clear message provides enough detail for more or less every situation where arguments are invalid.
STW
agreed, but i was just describing the options available. I definitely should have been more clear about the "preferred" method.
Scott M.
+13  A: 

I voted for Josh's answer, but would like to add one more to the list:

System.InvalidOperationException should be thrown if the argument is valid, but the object is in a state where the argument shouldn't be used.

Update Taken from MSDN:

InvalidOperationException is used in cases when the failure to invoke a method is caused by reasons other than invalid arguments.

Let's say that your object has a PerformAction(enmSomeAction action) method, valid enmSomeActions are Open and Close. If you call PerformAction(enmSomeAction.Open) twice in a row then the second call should throw the InvalidOperationException (since the arugment was valid, but not for the current state of the control)

Since you're already doing the right thing by programming defensively I have one other exception to mention is ObjectDisposedException. If your object implements IDisposable then you should always have a class variable tracking the disposed state; if your object has been disposed and a method gets called on it you should raise the ObjectDisposedException:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Update: To answer your follow-up: It is a bit of an ambiguous situation, and is made a little more complicated by a generic (not in the .NET Generics sense) data type being used to represent a specific set of data; an enum or other strongly typed object would be a more ideal fit--but we don't always have that control.

I would personally lean towards the ArgumentOutOfRangeException and provide a message that indicates the valid values are 1-12. My reasoning is that when you talk about months, assuming all integer representations of months are valid, then you are expecting a value in the range of 1-12. If only certain months (like months that had 31 days) were valid then you would not be dealing with a Range per-se and I would throw a generic ArgumentException that indicated the valid values, and I would also document them in the method's comments.

STW
Good point. This might explain the difference between invalid and unexspected input. +1
Daniel Brückner
Psst, I agree with you just wasn't going to steal your thunder. But since you pointed it out, I updated my answer
JoshBerke
A: 

Short answer:
Neither

Longer answer:
using Argument*Exception (except in a library that is a product on its on, such as component library) is a smell. Exceptions are to handle exceptional situation, not bugs, and not user's (i.e. API consumer) shortfalls.

Longest answer:
Throwing exceptions for invalid arguments is rude, unless you write a library.
I prefer using assertions, for two (or more) reasons:

  • Assertions don't need to be tested, while throw assertions do, and test against ArgumentNullException looks ridiculous (try it).
  • Assertions better communicate the intended use of the unit, and is closer to being executable documentation than a class behavior specification.
  • You can change behavior of assertion violation. For example in debug compilation a message box is fine, so that your QA will hit you with it right away (you also get your IDE breaking on the line where it happens), while in unit test you can indicate assertion failure as a test failure.

Here is what handling of null exception looks like (being sarcastic, obviously):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

Exceptions shall be used when situation is expected but exceptional (things happen that are outside of consumer's control, such as IO failure). Argument*Exception is an indication of a bug and shall be (my opinion) handled with tests and assisted with Debug.Assert

BTW: In this particular case, you could have used Month type, instead of int. C# falls short when it comes to type safety (Aspect# rulez!) but sometimes you can prevent (or catch at compile time) those bugs all together.

And yes, MicroSoft is wrong about that.

IMHO, exceptions should also be thrown when the called method cannot reasonably proceed. That includes the case when the caller has passed bogus arguments. What would you do instead? Return -1?
John Saunders