[c#] What exceptions should be thrown for invalid or unexpected parameters in .NET?

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?

This question is related to c# .net vb.net exception

The answer is


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.


argument exception.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException

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 unexpected if it is not valid for the current state of the system. So in the later case an InvalidOperationException is a reasonable choice.


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.


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.


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.


Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to vb.net

How to get parameter value for date/time column from empty MaskedTextBox HTTP 415 unsupported media type error when calling Web API 2 endpoint variable is not declared it may be inaccessible due to its protection level Differences Between vbLf, vbCrLf & vbCr Constants Simple working Example of json.net in VB.net How to open up a form from another form in VB.NET? Delete a row in DataGridView Control in VB.NET How to get cell value from DataGridView in VB.Net? Set default format of datetimepicker as dd-MM-yyyy How to configure SMTP settings in web.config

Examples related to exception

Connection Java-MySql : Public Key Retrieval is not allowed How to print an exception in Python 3? ASP.NET Core Web API exception handling Catching FULL exception message How to get exception message in Python properly What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean? what does Error "Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)" mean? Argument Exception "Item with Same Key has already been added" The given key was not present in the dictionary. Which key? sql try/catch rollback/commit - preventing erroneous commit after rollback