Coding Faster With dotNetTips Spargine 6 - Validating Arguments Made Easy

During my career as a software engineer, I have analyzed millions of lines of code for issues, and one of the top things that I see developers not doing is validating data coming into their types. This includes methods and properties. I understand why. It’s mundane work, but it’s very important since bad data in equals bad data out. If that bad data gets into the database, then it’s very difficult to fix. Also, if the code is not validating the data, then it’s not properly implementing encapsulation and therefore is not proper object-oriented programming.

In previously released versions of Spargine, I have had helper methods for this. For my work moving Spargine to .NET 6, I have completely rewritten these methods to make it even easier to validate data using fluent validation. I wish that the .NET team at Microsoft would add helper methods like this to .NET, but until then teams can use the extension methods in the Validator class in dotNetTips.Spargine.Core. The source code and NuGet packages can be found below.

Validating Methods and Properties

I will document all the methods currently available to validate parameters and show examples. All these methods return the data that is being validated so you can use these methods with others. Most also support a default value to be returned if validation fails. All the Exceptions thrown in these methods properly use ArgumentException or another exception that uses this class as its base class. If a custom error message is not supplied, then a default one will be used.

All the methods below have a parameter called paramName, but there is no need to use it. The methods automatically get the name by using the CallerArgumentExpression attribute.

Checking For Null

Before operating on any data from a call to a database or an API, etc., it’s very important to make sure it’s not null. This is a very common Exception that I see all the time in logs or bug reports. For this validation, you can use ArgumentNotNull(). This method supports <T> and Uri. Here is an example of how to use this method.

public static async Task<string> DownloadStringAsync(Uri address)
{
    address = address.ArgumentNotNull();   
    // Code removed for brevity
}

Validating Data is Not Null and Not Empty

To make sure that data is not null and not empty, you can use the ArgumentNotNullEmpty(). Currently, this method is overloaded for use with Guid and string. Here is an example of how to use it for a string.

internal static bool IsEmailAddress(this string input)
{
    return input.ArgumentNotNullOrEmpty().HasValue(Resources.RegexEmail, RegexOptions.IgnoreCase);
}

Validating Data with a Condition

If you need to validate data using a Boolean condition, you can use ArgumentMeetsCondition().

public static byte[] GenerateByteArray(double sizeInKb)
{
    sizeInKb = sizeInKb.ArgumentMeetsCondition(sizeInKb >= double.Epsilon, errorMessage: $"Size must be >= {double.Epsilon}.");
    // Code removed for brevity
}

Validating Data with a Regular Expression

If you need to validate data with a regular expression, then you can use the ArgumentMatched() method as shown below.

var expression = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
userEmail = userEmail.ArgumentMatched(expression);

Validating Collections

To ensure a collection has items in it before you try to use it, you can use ArgumentItemsExist(). Far too many times I’ve seen code like this, even today.

var person = personCollection[2];

If there aren’t items in the personCollection or it’s null, then this will cause an exception. This overloaded method supports IEnumerable<T>, IList<T>, and T[]. Here is an example of how to use ArgumentItemsExist().

public int CopyFiles(IEnumerable<FileInfo> files, DirectoryInfo destination)
{
    var list = files.ArgumentItemsExists().ToArray();
    // Code removed for brevity
}

One of these overloaded methods supports validating the number of items in the collection as shown below.

var people = people.ArgumentItemsExists(count: 10);

Ensuring Values Are in a Range

When validating integers, dates, and more you might want to ensure the parameter values are within a given lower and upper range. For this, you can use ArgumentInRange(). This method is overloaded to work with:

Byte DateTime DateTimeOffset Int32
Double Long Decimal DateOnly
TimeOnly String (validates string length)

This is an example on how to use ArgumentInRange().

public static Collection<T> Create(int capacity)
{
    capacity = capacity.ArgumentInRange(lower: 1, upper: int.MaxValue, defaultValue: 1);
    return new Collection<T>(capacity);
}

Making Sure Directories and Files Exist

It’s important to ensure that a directory or file exists before trying to access them. For this, you can use ArgumentExists(). Below is how to use it for a directory.

public static void MoveDirectory(DirectoryInfo source, DirectoryInfo destination, int retries = 10)
{
	source = source.ArgumentExists();
	//Code removed for brevity
} 

Validating Enums

One type that I never see developers validate is Enums. If somehow an invalid number is entered, then your code will cause an exception. To ensure this does not happen, use the ArgumentDefined() method as shown below.

public static IEnumerable<Type> FindDerivedTypes(DirectoryInfo path, SearchOption fileSearchType, Type baseType, Tristate classOnly)
{
    fileSearchType = fileSearchType.ArgumentDefined();
    // Code removed for brevity
}

Validating Types

The method ArgumentEquals () will validate that a type equals an expected type.

var person = RandomData.GeneratePerson<Person>();
var result = person.GetType().ArgumentEquals(typeof(Person));

Making Sure Spans are not Empty

To make sure that a ReadOnlySpan<T> and Span<T> are not empty before using it, you can use the ArgumentNotEmpty() method as shown below.

public static void FastProcessor<T>(this T[] list, Action<T> action)
{
    var collection = new ReadOnlySpan<T>(list).ArgumentNotEmpty();   
    // Code removed for brevity
}

Validating Collections are not Read Only

Before you try to add or remove items from a collection, it’s important to make sure that it is not read-only, since this will cause an exception. For this, you can use ArgumentNotReadOnly(). This method supports the IList<T> and ICollection<T> types as shown below.

public static void AddFirst<T>(this IList<T> list, T item)
{
    list.ArgumentNotReadOnly().Insert(index: 0, item);
}

Other Examples

Here are some other examples of how I am using these methods in Spargine.

Example 1

public static void FastLogger(this ILogger logger, LogLevel logLevel, string message, Exception ex)
{
   LoggingHelper.FastLogger(logger.ArgumentNotNull(),
     logLevel.ArgumentDefined(), message.ArgumentNotNullOrEmpty(), ex);
}

Example 2

public int Value
{
    get
    {
        return this._value;
    }
    init
    {
        this._value = value.ArgumentInRange(lower: 0);
    }
}

Example 3

public static void AddIf<T>(this ICollection<T> collection, T item, bool condition)
{
	collection = collection.ArgumentNotNull().ArgumentNotReadOnly();	
	//Code removed for brevity
}

Summary

I hope you will check out these methods to make it easy to validate parameters or any of the others in the assemblies. I am also looking for contributors to these .NET projects. If you have any comments or suggestions, please comment below.


McCarter Consulting
Software architecture, code & app performance, code quality, Microsoft .NET & mentoring. Available!