Take advantage of guard clauses in C# to eliminate unnecessary nested constructs and branches and enhance the readability and maintainability of your application’s code.

We always strive to write source code that is clean, maintainable, and devoid of any errors. However, we may often find that our application’s code is cluttered because we’ve used too many nested constructs or branches to validate our business rules. Enter guard clauses.
Guard clauses allow us to improve the readability of our code by eliminating the need of writing unnecessary conditional statements and nested code in our methods. In this article, we’ll examine how we can use guard clauses in C#.
To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create a console application project in Visual Studio 2022
First off, let’s create a .NET Core 9 console application project in Visual Studio 2022. Assuming you have Visual Studio 2022 installed, follow the steps outlined below to create a new .NET Core 9 console application project.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Click Next.
- In the “Additional information” window shown next, choose “.NET 9.0 (Standard Term Support)” as the framework version you would like to use.
- Click Create.
We’ll use this .NET 9 console application project to work with guard clauses in the subsequent sections of this article.
What is a guard clause?
A guard clause is a conditional check that validates conditions or input parameters specified in a method. If the conditions are not satisfied, the execution of the method stops prematurely, often by throwing an exception or using a return statement.
Guard clauses use the “fail fast” principle, terminating the flow of control of a program if input parameters are invalid or other conditions are not met. You can take advantage of guard clauses to short-circuit your methods before runtime errors occur.
Guard clauses provide an elegant way to ensure that values passed to the parameters of a method are valid. If invalid values are passed, the guard clause terminates the flow of control of the program, thereby preventing unexpected behavior. Typical examples of guard clauses include checking if an object is null or a string is empty.
Guard clauses in C# can benefit you in several ways:
- You can use guard clauses in your methods to prevent runtime exceptions by handling null reference exceptions.
- You can use guard clauses to validate input data and enforce data integrity by only processing valid inputs.
- You can use guard clauses to define preconditions for your methods and properties, enhancing the readability and maintainability of your code.
We’ll examine each of these aspects of guard clauses using code examples in the sections that follow.
Use a guard clause to avoid null reference exceptions in C#
Null reference exceptions are often encountered in applications when you use an object reference that has not been initialized. In this section, we will examine how we can use a guard clause to prevent such exceptions from being thrown at runtime. The following code snippet shows how you can check if the parameter of a method is null and throw an ArgumentNullException, preventing a null reference exception from being thrown at runtime and allowing the method to complete gracefully.
public void CheckIfNull(object obj)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj), "Method parameter cannot be null.");
}
//Other code
}
Use a guard clause to enforce input validation rules in C#
Input validation enables you to maintain data integrity by enforcing validation rules and constraints in your application. You can implement guard clauses in your application’s source code to allow only valid data to be processed by your application.
The following code example shows how you can use a guard clause in C# to prevent invalid input. Note how an exception is thrown if the input string is null or empty.
public void CheckIfNullOrEmpty(string str)
{
if(!string.IsNullOrEmpty(str))
{
throw new ArgumentException("Invalid data: The string passed in the method argument cannot be empty or null");
}
//Other code
}
Use a guard clause to enhance code readability and maintainability in C#
Guard clauses help you write maintainable and readable code by centralizing your application’s validation rules. They provide an elegant way to prevent unexpected behavior in your application’s code, making it consistent, organized, and easy to maintain.
Let us understand this with a code example. In the console application project we created earlier, create a new class named Author and enter the following code.
class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Email { get; set; }
}
The following code snippet illustrates how you can take advantage of the constructor of the Author class to validate input.
public Author(string firstName, string lastName, string address, string email)
{
if (string.IsNullOrEmpty(firstName))
{
throw new ArgumentException("First name cannot be null or empty", nameof(firstName));
}
if (string.IsNullOrEmpty(lastName))
{
throw new ArgumentException("Last name cannot be null or empty", nameof(lastName));
}
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Address cannot be null or empty", nameof(address));
}
if (string.IsNullOrEmpty(email))
{
throw new ArgumentException("Email cannot be null or empty", nameof(email));
}
}
As you can see, the preceding code snippet is quite verbose. We can make it much more concise and readable by using guard clauses. Note that the following code can replace all of the if statements used in the constructor of the Author class above.
Guard.NotNullOrEmpty(firstName, nameof(firstName),
"First name cannot be null or empty");
Guard.NotNullOrEmpty(lastName, nameof(lastName),
"Last name cannot be null or empty");
Guard.NotNullOrEmpty(address, nameof(address),
"Address cannot be null or empty");
Guard.NotNullOrEmpty(email, nameof(email),
"Email cannot be null or empty");
To assist you in implementing guard clauses into your C# programs, there are several libraries available. Two of the most commonly used are Guard.NET and Ardalis.GuardClauses. This code example assumes you’ve already installed the Guard.NET NuGet package in your project.
Should I use guard clauses or validation frameworks?
While you can use guard clauses to perform input validation, using them for validating user input is not recommended. That’s because guard clauses throw exceptions to terminate the flow of control of a program, and you want to avoid throwing exceptions every time user input data is not valid. Therefore you should use validation frameworks such as Fluent Validation to validate input data.
Remember, validation failure is not an exception, but an expected behavior. Moreover, by throwing exceptions and returning validation failure messages to the user, you will have to throw several exceptions, at least one per piece of input data. While validation frameworks are used to handle incorrect or invalid user input, guard clauses are typically used to prevent invalid data or unexpected behavior when creating instances of domain classes.
Takeaways
Guard clauses adhere to the single responsibility principle by isolating the validation logic from the application’s business logic. Each guard clause represents a specific validation check. You might have one guard clause that checks if a string variable of a method is null, another guard clause that checks if the minimum length is 10, and so on.
You can also enforce precondition checks for your methods and properties by using the “Parse, don’t validate” design pattern. This elegant approach enforces data integrity while keeping your application’s source code clean, concise, readable, and maintainable. I’ll discuss this design pattern in a future post here.