What are Guard Clauses?
A guard in general is to protect against the damage that may happen in the future.
In computer programming, a guard is a boolean expression that must evaluate to true if the program execution is to continue in the branch in question
wikipedia
Failing fast is a good strategy when writing code. It allows us to short-circuit the code execution. Guard clauses are a technique to achieve fast failing.
How to guard a method?
Let’s say we have the following method
bool SendEmail(string emailAddress) { return _repo.SendEmail(emailAddress); }
Given an email address, the SendEmail
method sends an email to the user.
But, what if we pass a NULL or EMPTY value to our method. We can raise an exception if the input is not valid or null or blank etc (not validating the email address at it may distract the intent)
Here is our revised code
bool SendEmail(string emailAddress) { if (string.IsNullOrWhiteSpace(emailAddress)) { throw new ArgumentNullException(nameof(emailAddress)); } return _repo.SendEmail(emailAddress); }
Throwing an ArgumentNullException
when the email address is null or whitespace is a guard here.
Great. This is all looking good.
What if we want to check the inputs for null or check if the list has values across the project and raise appropriate exceptions?
Well, we can write these checks all over the project but this leads to code duplication all over the place. Let’s see what libraries we have for guard clauses.
Available Guard Clauses Libraries
I found these 3 libraries which had more downloads on nuget.com
Library Name | Last Updated | Supported Frameworks |
---|---|---|
Ardalis.GuardClauses | 4 months ago | .NET 6, .NET Standard 2.0, .NET Framework 4.5.1 |
Dawn.Guard | 3/31/2020 | .NET Standard 1.0 |
Guard.Net | 9 months ago | .NET 5.0 |
For this article, I’ll make use of Ardalis.GuardClauses
library as it is supporting latest frameworks.
Refactoring our method using Ardalis.GuardClauses
Let’s try to refactor our SendEmail
method with Guard.Against.NullOrWhiteSpace
method.
bool SendEmail(string emailAddress) { Guard.Against.NullOrWhiteSpace(emailAddress, nameof(emailAddress)); return _repo.SendEmail(emailAddress); }
That’s it! We went from this
if (string.IsNullOrWhitespace(emailAddress)) { throw new ArgumentNullException(nameof(emailAddress)); }
to this
Guard.Against.NullOrWhiteSpace(emailAddress, nameof(emailAddress));
Looks good right.
Most used guard in my opinion: Guard.Against.Null
When supplying dependencies through class constructors we usually perform ??
(null-coalescing-operator) against the parameters to avoid throwing null reference exceptions. We do something like this.
public class UserManager { private readonly IUserRepository _userRepo; private readonly IDepartmentRepsitory _deptRepo; private readonly IEmailService _emailService; private readonly ILogger _logger; public UserManager(IUserRepository userRepo, IDepartmentRepsitory deptRepo, IEmailService emailService, ILogger logger) { _userRepo = userRepo ?? throw new ArgumentNullException(nameof(userRepo)); _deptRepo = deptRepo ?? throw new ArgumentNullException(nameof(deptRepo)); _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } }
With Guard.Against.Null
we can transform the above code into this.
public class UserManager { private readonly IUserRepository _userRepo; private readonly IDepartmentRepsitory _deptRepo; private readonly IEmailService _emailService; private readonly ILogger _logger; public UserManager(IUserRepository userRepo, IDepartmentRepsitory deptRepo, IEmailService emailService, ILogger logger) { _userRepo = Guard.Against.Null(userRepo, nameof(userRepo)); _deptRepo = Guard.Against.Null(deptRepo, nameof(deptRepo)); _emailService = Guard.Against.Null(emailService, nameof(emailService)); _logger = Guard.Against.Null(logger, nameof(logger)); } }
The code is now more concise when compared with null coalescing checks right?
Supported Guard clauses in Ardalis.GuardClauses
- 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 a 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)
Do we really need a library for Guard classes?
It depends. The library we used in this article packs a bunch of useful guard methods which we can use on a daily basis. Ex: NullOrWhiteSpace
, Zero
But, if we need only a couple of methods from Ardalis.GuardClauses
then I think it may not be useful to add it as a dependency to our project.
Let’s say we use only null reference guards in our project, then it’s better we can create a static method and utilize them everywhere.
We can write a static method that does the same thing as Guard.Against.Null
public static class GuardX { public static T ThrowIfNull<T>(T input, string message) { if (input is null) throw new ArgumentNullException(message); return input; } }
And while guarding, we can do this
_userRepo = GuardX.ThrowIfNull(userRepo, nameof(userRepo));
If we have to check if a collection is empty, we can write another static method and use it. Again, its upto you.
References
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
Pingback: Dew Drop – July 19, 2022 (#3725) – Morning Dew by Alvin Ashcraft
There is a new library for .net6+ that simplifies having to have a message as well. See here: https://github.com/amantinband/throw
Thank you Stuart. I’ll take a look. I’ve searched nuget.org with guard as search terms and I got those 3 libraries that I’ve mentioned in the article.
Pingback: The Morning Brew - Chris Alcock » The Morning Brew #3514
It is worth mentioning that in net6.0 a shortcut is available now for ArgumentNullException:
ArgumentNullException.ThrowIfNull(userRepo)
No need to pass a name of argument, it will be retrieved using
CallerArgumentExpression
attribute under the hood.Also one from Community Toolkit
https://docs.microsoft.com/en-us/windows/communitytoolkit/developer-tools/guard
https://github.com/CommunityToolkit/dotnet
James,
Thank you for your reply.
I saw community toolkit, it has several other things along with Guard clauses. May not be ideal for those who want to just use Guard clauses. If we want to use other features along with Guard clauses then it will be useful.