Application code must differentiate between exceptions caused by null/non-null arguments. This post shows you how to raise such exceptions.
Exceptions can be thrown either by the runtime or the application code. This can be caused by bad code, or by bad user input that has not been accounted for in the application's code. When any of these errors occur, the system catches the error and raises an exception.
The process of generating and signaling the error is referred to as throwing an exception. This is done using the throw
keyword followed by a new instance of a class derived from System.Exception
. There are standard exception types provided by the .NET framework that you can use rather than creating a custom exception.
In this post, I'll show you how to use the ArgumentNullException
and ArgumentOutOfRangeException
types, which are some of the standard exceptions in the framework.
The ArgumentOutOfRangeException
exception can be thrown when the argument passed to a method is not null
and contains a value that is not within the set of expected values. This exception type has the properties ParamName and ActualValue, which help us understand the cause of the exception. The ParamName
property identifies the parameter name of the invalid argument, and the ActualValue
property identifies the invalid value if a value is present.
The ArgumentOutOfRangeException
exception would normally result from developer error, and if the value of the argument is returned by a method call or input from the user before being passed to the method that throws the exception, you should validate arguments before passing them to the method.
Let's look at an example.
namespace MyApp
{
using System;
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Enter account name");
var name = Console.ReadLine();
Console.WriteLine("Enter opening balance");
var balance = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Opening account for {0}... \n", name);
var account = new Account(name, balance);
Console.WriteLine("Account opened for {0}", account.Name);
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex);
}
}
}
class Account
{
public Account(string name, int balance)
{
if (balance < 500)
throw new ArgumentOutOfRangeException(
nameof(balance),
balance,
"The account balance can't be less than 500");
Name = name;
Balance = balance;
}
public string Name { get; private set; }
public int Balance { get; private set; }
}
}
In the code above, we have an Account
class with properties Name
and Balance
, and a constructor that accepts name
and balance
as parameters. When this constructor gets a balance
less than 500
, it'll throw an ArgumentOutOfRangeException
exception with the parameter name that caused the exception, the value of the argument, and message explaining the cause of the error. In the static Main
method, we collect the name and balance from the user, then create an Account
object with that information.
If we run the application and enter a value less than 500 for the balance, we will get an exception. The information below is what we get when we run the code with 300 as the balance.
Enter account name
Jumbo
Enter opening balance
300
Opening account for Jumbo...
System.ArgumentOutOfRangeException: The account balance can't be less than 500
Parameter name: balance
Actual value was 300.
It is useful to differentiate between exceptions caused by null arguments and exceptions caused by arguments that are not null. The ArgumentNullException
exception type derives from the ArgumentException
class and is used to identify exceptions caused by null arguments. This exception is thrown when a null value is passed to a method argument and null is not allowed for that argument.
The code below is an example of how to use this exception type.
using System;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
try
{
var user = new User(null, 23);
}
catch (ArgumentNullException ex)
{
Console.WriteLine(ex);
}
}
}
class User
{
public User(string userName, int age)
{
if (userName == null)
throw new ArgumentNullException(
nameof(userName),
"username is invalid");
UserName = userName;
Age = age;
}
public string UserName { get; private set; }
public int Age { get; private set; }
}
}
Here we declared a User
class with properties UserName
and Age
. In the class constructor, we accept two parameters and assign them to the appropriate properties. Before the assignment, we check if userName
is null. If it is, we throw an ArgumentNullException
. We called the constructor of ArgumentNullException
that accepts the parameter name that caused the exception and a second parameter representing the exception message. You might have noticed we used the nameof
operator to get the string name of the parameter that caused the exception. Using the nameof
operator helps keep your code valid when renaming definitions.
If you run the code, it should throw an exception because userName
is null and also specify the parameter name to easily identify which parameter caused the exception.
An exception is thrown when an error is encountered in a running application. This could be a result of bad code or bad user input that has not been accounted for in the application's code.
In this post we looked at two standard exception types from the .NET framework, the ArgumentNullException
and ArgumentOutOfRangeException
types. The ArgumentOutOfRangeException
exception is thrown when the value of an argument is outside the expected range of values as defined by the invoked method. The ArgumentOutOfRangeException
exception normally results from developer error, so when dealing with this kind of error, be sure to read the value of the properties Message
, ParamName
, and ActualValue
. The ArgumentNullException
is thrown when a null
value is passed to a method that does not accept null values as valid input. They're provided so that application code can differentiate between exceptions caused by null arguments and exceptions caused by arguments that are not null.
Peter is a software consultant, technical trainer and OSS contributor/maintainer with excellent interpersonal and motivational abilities to develop collaborative relationships among high-functioning teams. He focuses on cloud-native architectures, serverless, continuous deployment/delivery, and developer experience. You can follow him on Twitter.