In method implementation, if the arguments passed by the caller are unexpected values (such as null or numbers out of range), the process must be stopped immediately instead of continuing. This is called “Fail Fast,” and it is an extremely important design philosophy for discovering bugs early and maintaining data consistency.
In C#, you can generate an exception at any time using the throw statement. This article explains how to detect invalid arguments and throw appropriate exceptions, using a “rectangle area calculation” in a geometry library as an example.
Basics of the throw Statement
To generate an exception, specify an instance of an exception object following the throw keyword.
throw new ExceptionClass(ErrorMessage);
When validating arguments, it is important to select the appropriate exception class depending on the situation.
- ArgumentException: General invalid arguments.
- ArgumentNullException: When an argument is
null. - ArgumentOutOfRangeException: When an argument is outside the allowable range (e.g., negative numbers).
Practical Code Example: Rectangle Area Calculation and Validation
The following code is a method that receives the “width” and “height” of a rectangle and returns the area. If a “number less than or equal to 0,” which is physically impossible, is passed, it throws an ArgumentOutOfRangeException and interrupts the process.
using System;
namespace GeometryLibrary
{
class Program
{
static void Main()
{
try
{
// Normal case
double area1 = CalculateRectangleArea(10.5, 5.0);
Console.WriteLine($"Area 1: {area1}");
// Abnormal case (Specifying a negative number for width)
// An exception occurs here, and control moves to the catch block.
double area2 = CalculateRectangleArea(-5.0, 10.0);
Console.WriteLine($"Area 2: {area2}"); // This line is not executed
}
catch (ArgumentOutOfRangeException ex)
{
// Display error details
Console.WriteLine("--- An error occurred ---");
Console.WriteLine($"Message: {ex.Message}");
Console.WriteLine($"Parameter Name: {ex.ParamName}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected Error: {ex.Message}");
}
}
/// <summary>
/// Calculates the area of a rectangle.
/// </summary>
/// <param name="width">Width (positive number)</param>
/// <param name="height">Height (positive number)</param>
/// <returns>Area</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when width or height is 0 or less.</exception>
static double CalculateRectangleArea(double width, double height)
{
// Validation: Invalid if width is 0 or less
if (width <= 0)
{
// Constructor for ArgumentOutOfRangeException
// 1st arg: Parameter name (using nameof operator makes it refactoring-safe)
// 2nd arg: The actual value passed
// 3rd arg: Error message
throw new ArgumentOutOfRangeException(nameof(width), width, "Width must be a value greater than 0.");
}
// Validation: Invalid if height is 0 or less
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), height, "Height must be a value greater than 0.");
}
// Calculation process (Only safe values reach here)
return width * height;
}
}
}
Execution Result
Area 1: 52.5
--- An error occurred ---
Message: Width must be a value greater than 0. (Parameter 'width')
Actual value was -5.
Parameter Name: width
Technical Points and Best Practices
1. Selecting the Appropriate Exception Class
Instead of simply using Exception or ArgumentException, you should choose a class that specifically represents the situation.
- Use ArgumentOutOfRangeException if the range of the value is the issue (negative value, out of index, etc.).
- Use ArgumentException if the format or consistency of the value is the issue.
2. Utilizing the nameof Operator
When throwing an exception, you specify the parameter name to identify which argument caused the issue. At this time, it is strongly recommended to use nameof(width) instead of writing “width” as a string. This ensures that if you change the argument name (refactoring) in the future, the correction is automatically reflected, maintaining code consistency.
3. Improving Readability with Guard Clauses
Checking for invalid values at the beginning of a method and performing an early return (or throwing an exception) is called a “Guard Clause.” This prevents the normal processing logic (return width * height;) from being nested deep within indentations, improving code readability.
4. Simplification in .NET 8 and Later
In .NET 8 (C# 12) and later, helper methods like ArgumentOutOfRangeException.ThrowIfLessThanOrEqual have been added, allowing you to perform checks in a single line without writing an if statement.
// Example in .NET 8 or later
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(width, 0, nameof(width));
Summary
Strictly checking method preconditions using the throw statement is the first step in robust class design. By implementing it so that it “reports a specific error immediately if a strange value comes” rather than “does not work if a strange value comes,” you significantly improve ease of debugging and system reliability.
