Here is how to throw an exception in C#, and when you may not want to throw exceptions.
The sample code will save an inventory item object, but not allow the save if the item’s Name property is an empty string, or the Quantity property is a negative number. We’ll do one way with throwing exceptions, and one way without.
Keep in mind, you normally only want to throw an exception is something unexpected is happening. Common situations, like a user entering an invalid value, may better be handled without exceptions. In general, throwing an exception should be your last choice – not your first choice.
How to throw an exception in C#
In this sample code, we’ll have a simple InventoryItem class and prevent saving objects with invalid property values by raising exceptions in the SaveItem() function.
namespace ThrowException; public class InventoryItem { public string Name { get; } public int Quantity { get; } public InventoryItem(string name, int quantity) { Name = name; Quantity = quantity; } }
The SaveItem() function in the code below will throw an exception if the item’s Name property is an empty string or if its Quantity property is less than 0.
Throwing the exception lets the calling function know there was a problem preventing the item from being saved. However, there are some problems.
Notice that, if the InventoryItem object has multiple problems with property values, only the first exception will be raised in SaveItem(). When you throw an exception, the program does not run any further in the function where you threw it. There may be times when it better to be able to report back to the user (or calling function) that there were multiple problems. This is a situation when you may want to not throw an exception.
using ThrowException; Console.WriteLine("Starting"); try { //InventoryItem item = new InventoryItem("Robot", 123); //InventoryItem item = new InventoryItem("Robot", -5); InventoryItem item = new InventoryItem("", -5); SaveItem(item); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); } Console.WriteLine("Finished"); Console.ReadLine(); // Function that validates data before saving it void SaveItem(InventoryItem item) { if (string.IsNullOrEmpty(item.Name)) { throw new ArgumentNullException(nameof(item.Name)); } if (item.Quantity < 0) { throw new ArgumentOutOfRangeException(nameof(item.Quantity), "Value must be zero or higher"); } // Code to save valid data would be here }
When you may not want to throw an exception
In the code in the first sample, everything is happening inside the same program. The InventoryItem class, the code that instantiates InventoryItem objects, the code that calls the SaveItem() function, and the SaveItem() function are all visible to each other. So, we can use another method to prevent saving bad data – without using exceptions.
In the BetterInventoryItem class, we have two properties to handle letting us know if the object is valid for saving: the Boolean IsValid property and the List<string> ValidationErrors property. When the object is instantiated, we’ll have the constructor set these two new properties.
This way, we can check if the object is valid for saving, before calling the SaveBetterItem() function – letting us remove the “throw new exception” code from the saving function.
Here’s the code for the BetterInventoryItem class that populates properties with information about the object’s validity.
namespace ThrowExceptionBetter; public class BetterInventoryItem { public string Name { get; } public int Quantity { get; } public bool IsValid { get; } public List<string> ValidationErrors { get; } = new List<string>(); public BetterInventoryItem(string name, int quantity) { Name = name; Quantity = quantity; IsValid = true; if (string.IsNullOrWhiteSpace(name)) { ValidationErrors.Add("'Name' cannot be empty"); IsValid = false; } if (quantity < 0) { ValidationErrors.Add("'Quantity' cannot be less than zero"); IsValid = false; } } }
In this code, we check the object’s IsValid property, and don’t allow calling SaveBetterItem() function is the object is not valid. This lets us eliminate throwing exceptions.
using ThrowExceptionBetter; Console.WriteLine("Starting"); //BetterInventoryItem betterItem = new BetterInventoryItem("Robot", 123); //BetterInventoryItem betterItem = new BetterInventoryItem("Robot", -5); BetterInventoryItem betterItem = new BetterInventoryItem("", -5); if (betterItem.IsValid) { SaveBetterItem(betterItem); } else { Console.WriteLine("BetterInventoryItem is not valid. Please correct and retry"); foreach (string validationError in betterItem.ValidationErrors) { Console.WriteLine(validationError); } } Console.WriteLine("Finished"); Console.ReadLine(); // Function that validates data before saving it void SaveBetterItem(BetterInventoryItem betterItem) { // Code to save valid data would be here }
When to throw exceptions
Exceptions should generally be used for unusual, unexpected situations – like if you try to write a file to disk, but the program cannot access the disk. Things like users entering invalid data should be expected and can often be handled without exceptions – especially if all the code is in the same project/solution.
Exceptions may be more appropriate if some other program is calling your program, like in a web API. You can’t be sure the data your API receives is valid. So, it may be better to have the API return an error response that includes an exception.