Introduction
Ardalis.GuardClauses
is a package in .NET created by Steve Smith (also known by his alias, Ardalis), who is an experienced software architect and developer. The package is designed to provide a simple, fluent API for adding guard clause validations to your code by allowing you to add a single line of code where traditionally, several lines of code would have been used. Guard clauses are a programming approach used to improve code clarity and reduce the possibility of runtime errors by validating input and state before executing the logic of a method or function.
Key Features of Ardalis.GuardClauses
:
- Simplified Validation: It simplifies the process of validating method arguments and makes the code cleaner and more readable.
- Fluent API: Offers a fluent API for expressing validations, which aligns well with modern C# coding practices. Over the last few projects I have worked on, I implemented Guard Clauses alongside FluentValidation. Using both together reduces the ‘clutter’ within the system, making it more readable, and it helps others when they are onboarded onto these projects.
- Extensibility: This design actively supports easy extension, enabling developers to seamlessly integrate their custom guard clauses using a clean, extensible model. Developers can create both simple and complex clauses, storing them in reusable extension classes for efficient use across different parts of their code.
- Common Validations: Includes a variety of common validations such as null checks, out-of-range checks, and more.
- Reduction of Boilerplate Code: Helps to reduce boilerplate validation code, focusing the developer’s attention on the core logic. I am always surprised at how much cleaner the code appears when implementing these clauses. If nothing else, this helps me by reducing the load on my eyes and my brain!
Example Usage:
To use Ardalis.GuardClauses
, you first need to install the NuGet package and it is done like this:
dotnet add package Ardalis.GuardClauses
You can also use the Nuget command line or package manager if you are using Visual Studio. This will install the latest version of the guard Nuget package into your project or projects and allow them to be used.
Then, you can use it in your code like this:
using Ardalis.GuardClauses;
public class MyClass
{
public void MyMethod(string input)
{
Guard.Against.NullOrWhiteSpace(input, nameof(input));
// Method logic goes here
}
}
In this example, the Guard.Against.NullOrWhiteSpace
method checks if the input
string is null, empty, or consists only of white-space characters. If the check fails, it throws an ArgumentException
, preventing the method from executing with invalid data.
Here is another example that contains normal guard clauses as most developers write them. Even for two checks, there is quite a bit of code that makes understanding what the constructor is doing more difficult: –
public class Engine
{
public Engine(int horsePower, string cylinderLayout, FuelType fuelType)
{
if (horsePower < 0)
{
throw new ArgumentException("Value cannot be negative", nameof(horsePower));
}
if (string.IsNullOrWhiteSpace(cylinderLayout))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(cylinderLayout));
}
// Additional code
}
}
It is possible to make this code considerably cleaner by using the Guard Clauses library as can be seen below: –
using Ardalis.GuardClauses;
public class Engine
{
public Engine(int horsePower, string cylinderLayout, FuelType fuelType)
{
Guard.Against.Negative(horsePower, nameof(horsePower));
Guard.Against.NullOrWhiteSpace(cylinderLayout, nameof(cylinderLayout));
// Additional code
}
}
This approach significantly aids in making the guard clauses less intrusive in the code. Essentially, it allows them to blend seamlessly into the background. As a result, the developer can focus more on the primary purpose or the true intentions behind the constructor. The constructor, which is a key part of object-oriented programming, is meant to set up an object when it’s created. When the guard clauses are less obtrusive, it becomes much easier for developers to understand and maintain the core logic of the constructor. This clarity is crucial for effective programming, as it ensures that the main objectives of the code are not overshadowed by necessary but secondary validation checks. By simplifying these aspects, the developer’s attention can remain on the essential functionalities and design principles intended in the construction of the object.
Supported Guard Clauses
- Guard.Against.Null (throws if input is null)
- Guard.Against.NullOrEmpty (throws if string, guid or array input is null or empty)
- Guard.Against.NullOrWhiteSpace (throws if string input is null, empty or whitespace)
- Guard.Against.OutOfRange (throws if integer/DateTime/enum input is outside a provided range)
- Guard.Against.EnumOutOfRange (throws if an enum value is outside a provided Enum range)
- Guard.Against.OutOfSQLDateRange (throws if DateTime input is outside the valid range of SQL Server DateTime values)
- Guard.Against.Zero (throws if number input is zero)
- Guard.Against.Expression (use any expression you define)
- Guard.Against.InvalidFormat (define allowed format with a regular expression or func)
- Guard.Against.NotFound (similar to Null but for use with an id/key lookup; throws a
NotFoundException
)
Extending with your own Guard Clauses
Adding new safety checks, or Guard Clauses, to the library is quite easy. The person who made the library planned for this.
Below is an example explaining how to make a new Guard Clause. It uses something called the IGuardClause interface and adds a special method to the FooGuard class. In this example, if a string in lowercase is equal to “foo”, the program will give an error. The part about how to use this shows that you can apply this Guard Clause with or without mentioning the name of the parameter (the specific bit of information you’re checking). However, I find it is usually better to include the parameter name because it makes fixing any problems in the code easier.
// Using the same namespace will make sure your code picks up your
// extensions no matter where they are in your codebase.
namespace Ardalis.GuardClauses
{
public static class FooGuard
{
public static void Foo(this IGuardClause guardClause,
string input,
[CallerArgumentExpression("input")] string? parameterName = null)
{
if (input?.ToLower() == "foo")
throw new ArgumentException("Should not have been foo!", parameterName);
}
}
}
// Usage
public void SomeMethod(string something)
{
Guard.Against.Foo(something);
Guard.Against.Foo(something, nameof(something)); // optional - provide parameter name
}
Handling Guard Clauses Exceptions
It’s important that the exceptions thrown by guard clauses are not caught and handled in the same method as the guard clauses are used. These exceptions typically indicate fundamental issues with the input data or logic, and catching them might mask underlying problems. Instead, you should allow these exceptions to bubble up the call stack to be handled at a higher level or logged for further investigation.
Conclusion
Overall, Ardalis.GuardClauses is an extremely useful tool for .NET developers. It greatly simplifies the process of implementing guard clauses, which are essential for checking and validating data.
By using this tool, developers can more easily ensure their code is robust, meaning it’s strong and less likely to break. Additionally, it helps in creating code that is resistant to errors. This is particularly important because it means the software is more reliable and trustworthy.
Furthermore, the use of Ardalis.GuardClauses promotes good coding practices, encouraging developers to think proactively about potential issues and handle them effectively. In essence, this tool not only streamlines the coding process but also enhances the overall quality, readability, and reliability of the software developed.
The github site for guard clauses can be found here: – https://github.com/ardalis/GuardClauses