In this article, we discuss how to create guard clauses, how they differ from user input validation, and how to improve our guards by writing clean guard clauses leveraging the GuardClauses library.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in!

What are Guard Clauses? – A Simple Example

A guard clause ensures that parameters meet certain conditions. If conditions are not met the executing method is exited either using a return statement or, most commonly, throwing an Exception. Unlike user input validation, where we expect wrong input, guard clauses prevent unexpected behavior from occurring when we instantiate domain objects.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Leveraging guard clauses as early as possible will enable us to short-circuit the method before any system resources are wasted performing unneeded operations. 

Some common guard clauses check if an object we pass is null, a string parameter is empty or whitespace, and an integer value is negative or zero. 

Let’s illustrate this with a simple example:

public class Car
{
    public Car(Engine engine)
    {
        if (engine == null)
        {
            throw new ArgumentNullException(nameof(engine), "Engline cannot be null");
        }

        Engine = engine;
    }

    public Engine Engine { get; private set; }
}

Here, we create a simple Car class that has one property of Engine type, we also pass the engine as a parameter to the constructor. A car without an engine is no car at all so we check if the engine parameter is null, and if it is – we throw an ArgumentNullException. This way we make sure that the Car objects we create will always have a valid Engine.

Next, we define our Engine:

public class Engine
{
    public Engine(int horsePower, int cylinders, string cylinderLayout, int topSpeed, FuelType fuelType)
    {
        if (horsePower < 0)
        {
            throw new ArgumentException("Value cannot not be negative", nameof(horsePower));
        }

        if (cylinders <= 0)
        {
            throw new ArgumentException("Value cannot be negative or zero", nameof(cylinders));
        }

        if (string.IsNullOrWhiteSpace(cylinderLayout))
        {
            throw new ArgumentException("Value cannot be null or whitespace.", nameof(cylinderLayout));
        }

        if (topSpeed == 0)
        {
            throw new ArgumentException("Value cannot be zero", nameof(topSpeed));
        }

        if (fuelType != FuelType.Diesel || fuelType != FuelType.Petrol)
        {
            throw new ArgumentException("Fuel type must be either diesel or petrol", nameof(fuelType));
        }

        HorsePower = horsePower;
        Cylinders = cylinders;
        CylinderLayout = cylinderLayout;
        TopSpeed = topSpeed;
        FuelType = fuelType;
    }

    public int HorsePower { get; }
    public int Cylinders { get; }
    public string CylinderLayout { get; }
    public int TopSpeed { get; }
    public FuelType FuelType { get; }
}

In a new file, we create the definition of our Engine class – it has five properties. First, we have HorsePower, Cylinders,and TopSpeed of integer type. Then we continue with CylinderLayout of string type. Finally, we have a FuelTypeproperty of FuelType type, which is a simple enumeration containing two values – Petrol and Diesel. We pass the values of those properties to the constructor when creating an object of Engine type.

We also have five if checks that throw an ArgumentException with a custom message when any of our passed parameters doesn’t meet a particular condition. This is a good way of making sure that our objects always contain valid data and behave as expected.

A downside to the common if approach is that for objects with many properties we risk overly long sections of guard clauses which reduce our code readability.

Let’s see how we can improve that.

Install GuardClauses Package

GuardClauses is a NuGet package developed to make guard clauses more readable and easy to understand. It also comes with various pre-defined clauses. Among them are guards against Null, NullOrWhiteSpace, and OutOfRange, to name a few.

Let’s install the GuardClauses package through .NET CLI:

dotnet add package Ardalis.GuardClauses

How to Use Pre-defined Guard Clauses?

Now that we have the library in our project, let’s make use of its pre-defined clauses:

public class Car
{
    public Car(Engine engine)
    {
        Engine = Guard.Against.Null(engine);
    }

    public Engine Engine { get; private set; }
}

In our Car class, we will remove the null check and use the Guard.Against.Null method we get from the Ardalis.GuardClauses namespace (don’t forget to add it to your using). To instantiate the Car class we pass it the engine parameter that we receive in the constructor. If engine is null we will receive an ArgumentNullException. If engine passes the null check its value is assigned to our Engine property.

Let’s pass a null value to our constructor and check the result:

Value cannot be null. (Parameter 'engine')

We get an Exception with a default message that gives us valuable information, but we can improve it:

public Car(Engine engine)
{
    Engine = Guard.Against.Null(engine, nameof(engine), "Engine cannot be null!");
}

Using an overload of the Null method, we pass the engine parameter, its name using nameof, and a custom message.

Let’s pass another null and check the result:

Engine cannot be null! (Parameter 'engine')

We can see that we get the message we defined, neat!

This is already an improvement, but let’s see what it does with larger guard clause blocks:

public class Engine
{
    public Engine(int horsePower, int cylinders, string cylinderLayout, int topSpeed, FuelType fuelType)
    {
        HorsePower = Guard.Against.Negative(horsePower);
        Cylinders = Guard.Against.NegativeOrZero(cylinders);
        CylinderLayout = Guard.Against.NullOrWhiteSpace(cylinderLayout);
        TopSpeed = Guard.Against.Zero(topSpeed);
        FuelType = Guard.Against.EnumOutOfRange(fuelType);
    }

    public int HorsePower { get; }
    public int Cylinders { get; }
    public string CylinderLayout { get; }
    public int TopSpeed { get; }
    public FuelType FuelType { get; }
}

We replace our guard clauses in the constructor of the Engine class with the pre-defined ones from the GuardClausespackage. The Negative method will guard for any value below 0. NegativeOrZero will only accept positive numbers. Zero will throw and Еxception only if the parameter is 0. NullOrWhiteSpace will guard us against null, empty, or white space string values. EnumOutOfRange will prevent us from passing an invalid enum value to our constructor.

Updating our Car class had made a minor improvement, but here we see the awesome power of the package – we were able to remove over 20 lines of code!

But there is more we can do with this package.

How To Create Custom Guard Clauses?

When designing the GuardClauses library, Steve Smith provided us with the opportunity to create our custom guard clauses:

public static class PetrolEngineGuard
{
    public static FuelType PetrolEngine(this IGuardClause guardClause, FuelType fuelType)
    {
        if (fuelType == FuelType.Petrol)
        {
            throw new ArgumentException("Fuel type cannot be petrol!", nameof(fuelType));
        }

        return fuelType;
    }
}

To guard against Petrol-fueled engines, we create a static PetrolEngineGuard class that has one staticPetrolEngine method with a return type of FuelType. The method takes in an IGuardClause preceded by the thiskeyword, which is needed for creating extension methods, and a fuelType parameter of FuelType type.

The custom guard clause we create will throw an ArgumentException with the message Fuel type cannot be petrol! if the value of the fuelType parameter is Petrol. Otherwise, we return its value. Make sure you have the PetrolEngineGuardclass in the Ardalis.GuardClauses namespace otherwise the extension method might not work.

Now, let’s use our clause:

public Engine(int horsePower, int cylinders, string cylinderLayout, int topSpeed, FuelType fuelType)
{
    HorsePower = Guard.Against.Negative(horsePower);
    Cylinders = Guard.Against.NegativeOrZero(cylinders);
    CylinderLayout = Guard.Against.NullOrWhiteSpace(cylinderLayout);
    TopSpeed = Guard.Against.Zero(topSpeed);
    FuelType = Guard.Against.PetrolEngine(fuelType);
}

In the constructor of our Engine class, we replace the Guard.Against.EnumOutOfRange method with Guard.Against.PetrolEngine and that’s it – clean and simple.

Let’s pass Petrol as FuelType and examine the result:

Fuel type cannot be petrol! (Parameter 'fuelType')

We receive an expected ArgumentException that the “Fuel type cannot be petrol”.

Conclusion

In this article, we have learned what guard clauses are and how to use them. More importantly, we have learned about the GuardClauses package. It provides us the capability of writing clean guards both with pre-defined and user-defined clauses – a valuable thing that improves our code readability.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!