GuardRequires 2.0.0-alpha

This is a prerelease version of GuardRequires.
There is a newer version of this package available.
See the version list below for details.
dotnet add package GuardRequires --version 2.0.0-alpha                
NuGet\Install-Package GuardRequires -Version 2.0.0-alpha                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GuardRequires" Version="2.0.0-alpha" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add GuardRequires --version 2.0.0-alpha                
#r "nuget: GuardRequires, 2.0.0-alpha"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install GuardRequires as a Cake Addin
#addin nuget:?package=GuardRequires&version=2.0.0-alpha&prerelease

// Install GuardRequires as a Cake Tool
#tool nuget:?package=GuardRequires&version=2.0.0-alpha&prerelease                

Guard/Requires

Table of Contents

Introduction

Guard/Requires is a library of guard clauses inspired by the Design By Contract concepts first introduced by Bertrand Meyer in the Eiffel programming language and also by Microsoft's Code Contracts (no longer supported by .NET 5 or higher).

Guard/Requires provides a broad range of guard options beyond the usual null checks. You can specify guards against limits (RequiresGreaterThan, RequiresLessThanOrEqualTo, RequiresValueBetween, etc.), enum values (RequiresEnumIsDefined, RequiresHasFlag), string values (RequiresMaxLength, RequiresNotNullOrWhitespace, RequiresRegexMatch, etc.), lambda conditions (RequiresConditionIsTrue) and more.

Guards That State Requirements

Typical guard clauses are written to test for the inverse of the method requirements. For example, if a method requires that x is not null then

if (x is null) 
{
   throw new ArgumentNullException();
}

Guard/Requires uses a naming convention that states the actual requirement, so the equivalent in Guard/Requires would be

x.RequiresNotNull();

with the inverse test performed in the RequiresNotNull method. This lets developers clearly state the requirements for a method with a block of Requires... statements and contributes to cleaner, self documenting code.

Guards vs Validation

While there are many similarities between using guard clauses and using a validation library such as FluentValidation there are some subtle distinctions that make one more appropriate than the other depending on the circumstances.

Validation is most appropriate when invalid inputs are expected and can be handled by the normal code flow. Guard methods are most appropriate when invalid inputs are exceptional cases that can not be handled without aborting the current code flow. In terms of a REST API, failed validation typically results in a 400 Bad Request response while a failed guard would result in a 500 Internal Server Error.

Typically, validation is used by the public facing layers of a system and guards are used by internal code. An ideal use for Guard/Requires is in the constructor of domain objects to ensure that all the inputs to the domain object meet the domain requirements.

Chaining Requirements

Guard/Requires uses a fluent design with extension methods that return the original value after each test is performed which allows multiple requirements to be chained together. When Guard/Requires is used in constructors, the return value from the requirement method(s) can be assigned to a class property in the same statement that ensures the incoming value meets the class requirements.

Custom Exceptions

Each Requires... method by default will throw one of the standard exceptions of the type that is most common for that requirement (for example, RequiresNotNull will throw an ArgumentNullException by default). In addition, each Requires... method has an optional getException parameter that is a delegate that allows you provide a custom exception to be thrown instead.

The signature of the getException delegate is

Func<String?, Dictionary<String, Object>, Exception>

The first parameter is the optional custom message that passed to the Requires... method. The second parameter is a dictionary of values supplied by the Requires... method. Typical entries will be a description of the failed requirement, the name of the parameter or value that failed the requirement and the actual value of the parameter or value that failed the requirement. (The default exceptions throw by the Requires... methods will contain the contents of the dictionary in the exception's Data property.) The return value is the exception to be thrown.

An example of using a custom exception:

// Custom exception class, including static method to create a new exception 
// suitable for use as the getException parameter of a Requires... method.
public class CustomException : Exception
{
   public CustomException(String message)
      : base(message) { }

   public override String Message => base.Message ?? "default message";

   public static CustomException GetException(
      String? customMessage,
      Dictionary<String, Object> data)
   {
      var exception = new CustomException(customMessage!);
      foreach (var item in data)
      {
         exception.Data[item.Key] = item.Value;
      }

      return exception;
   }
}

// Custom exception
String value = null;
value.RequiresNotNull(getException: CustomException.GetException);

// Custom exception and message
var offset = -1;
offset.RequiresGreaterThanZero("invalid offset", CustomException.GetException);

Note that the CustomException class in the previous example is used in method specific examples below.

ExceptionFactory Class

If a getException delegate is not supplied, Guard/Requires uses one of the factory methods in the ExceptionFactory class to generate the exception that is thrown when the requirement fails. ExceptionFactory was scoped for internal use only in 1.x releases of GuardRequires but scoped as public in 2.x and later releases. ExceptionFactory exposes the following static methods:

  • GetArgumentException
  • GetArgumentNullException
  • GetArgumentOutOfRangeException
  • GetFormatException
  • GetInvalidOperationException
  • GetNotSupportedException

You can specify one of these methods as the getException delegate in cases where you want to override the default exception thrown by a requirement but don't want to create a custom exception.

This is an example of overriding the default ArgumentException thrown by RequiresRegexMatch with a FormatException.

email.RequiresRegexMatch(_emailRegex, "Invalid email format", ExceptionFactory.GetFormatException);

A Full Example

public enum AddressType
{
   Home,
   Mailing,
   Billing,
   Shipping
}

public class Address
{
   public const Int32 AddressMaxLength = 30;
   public const Int32 CityMaxLength = 25;
   public const Int32 StateProvinceMinLength = 2;
   public const Int32 StateProvinceMaxLength = 2;
   public const Int32 PostalCodeMaxLength = 10;

   public Address(
      AddressType addressType,
      String addressLine1,
      String? addressLine2,
      String city,
      String stateProvince,
      String postalCode)
   {
      AddressType = addressType.RequiresEnumIsDefined();
      AddressLine1 = addressLine1
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(AddressMaxLength);
      AddressLine2 = addressLine2!.RequiresMaxLength(AddressMaxLength);
      City = city
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(CityMaxLength);
      StateProvince = stateProvince
         .RequiresNotNullOrWhiteSpace()
         .RequiresMinLength(StateProvinceMinLength)
         .RequiresMaxLength(StateProvinceMaxLength);
      PostalCode = postalCode
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(PostalCodeMaxLength);
   }

   public AddressType AddressType { get; }

   public String AddressLine1 { get; }

   public String? AddressLine2 { get; }

   public String City { get; }

   public String StateProvince { get; }

   public String PostalCode { get; }
}

public class Person
{
   public const Int32 NameMaxLength = 20;
   public const Int32 EmailAddressMaxLength = 254;

   private static readonly Regex EmailAddressRegex = new(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");

   public Person(
      String firstName,
      String lastName,
      String emailAddress,
      List<Address> addresses)
   {
      FirstName = firstName
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(NameMaxLength);
      LastName = lastName
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(NameMaxLength);
      EmailAddress = emailAddress
         .RequiresNotNullOrWhiteSpace()
         .RequiresMaxLength(EmailAddressMaxLength)
         .RequiresRegexMatch(EmailAddressRegex);
      Addresses = addresses
         .RequiresNotNull()
         .RequiresNotEmpty()
         .RequiresDistinct(x => x.AddressType);
   }

   public String FirstName { get; }

   public String LastName { get; }

   public String EmailAddress { get; }

   public IReadOnlyCollection<Address> Addresses { get; }
}

Using Guard/Requires

Initialization Requirements

RequiresNotNull

Use RequiresNotNull to express a requirement that a value not be null.

By default RequiresNotNull will throw an ArgumentNullException. The data collection supplied to the getException method will contain two entries with keys "Description" and "ParamName".

Examples:

HashSet<String> items = null!;

// Throws ArgumentNullException with default message.
items.RequiresNotNull();

// Throws ArgumentNullException with custom message.
items.RequiresNotNull("items may not be null");

// Throws CustomException with default message.
items.RequiresNotNull(getException: GetCustomException);

// Throws CustomException with custom message.
items.RequiresNotNull("items may not be null", GetCustomException);

RequiresNotDefault

Use RequiresNotDefault to express a requirement that a value not be the default for the values's type.

By default RequiresNotNull will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var offset = default(Int32);

// Throws ArgumentException with default message.
offset.RequiresNotDefault();

// Throws ArgumentException with custom message.
offset.RequiresNotDefault("offset may not be default");

// Throws CustomException with default message.
offset.RequiresNotDefault(getException: CustomException.GetException);

// Throws CustomException with custom message.
offset.RequiresNotDefault("offset may not be default", CustomException.GetException);

Range Requirements

RequiresGreaterThan

Use RequiresGreaterThan to express a requirement that a numeric value must be > a lower limit.

By default RequiresGreaterThan will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "LimitExpression" and "LimitValue".

RequiresGreaterThan has an optional comparer parameter that allows you to provide an IComparer<T> to use when comparing the value to the lower bound. If not supplied then the comparer parameter defaults to Comparer<T>.Default.

Examples:

var minimumDelta = 0.1M;
var minLetter = "a";
var minimumMultiplier = 2;

var delta = 0.001M;
var multiplier = 1;
var value = "Z";

// Default comparer, message and exception. Throws ArgumentOutOfRangeException with default
// message.
delta.RequiresGreaterThan(minimumDelta);

// User specified comparer, default message and exception. Throws ArgumentOutOfRangeException 
// with default message.
value.RequiresGreaterThan(minLetter, comparer: StringComparer.OrdinalIgnoreCase);

// Default comparer and exception with custom message. Throws ArgumentOutOfRangeException 
// with custom message.
delta.RequiresGreaterThan(minimumDelta, customMessage: "delta must be > 0.1");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
multiplier.RequiresGreaterThan(minimumMultiplier, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
multiplier.RequiresGreaterThan(minimumMultiplier, "multiplier must be > 2", CustomException.GetException);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
value.RequiresGreaterThan(minLetter, "value is out of range", CustomException.GetException, StringComparer.OrdinalIgnoreCase);

RequiresGreaterThanZero

Use RequiresGreaterThanZero to express a requirement that a numeric value must be > zero.

By default RequiresGreaterThanZero will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

RequiresGreaterThanZero is supported for types that implement INumber<T>.

Examples:

var length = -0.5;

// Throws ArgumentOutOfRangeException with default message.
length.RequiresGreaterThanZero();

// Throws ArgumentOutOfRangeException with custom message.
length.RequiresGreaterThanZero("length must be > zero");

var offset = 0;

// Throws CustomException with default message.
offset.RequiresGreaterThanZero(getException: CustomException.GetException);

// Throws CustomException with custom message.
offset.RequiresGreaterThanZero("offset must be > 0", CustomException.GetException);

RequiresGreaterThanOrEqualTo

Use RequiresGreaterThanOrEqualTo to express a requirement that a numeric value must be >= a lower limit.

By default RequiresGreaterThanOrEqualTo will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "LimitExpression" and "LimitValue".

RequiresGreaterThanOrEqualTo has an optional comparer parameter that allows you to provide an IComparer<T> to use when comparing the value to the lower bound. If not supplied then the comparer parameter defaults to Comparer<T>.Default.

Examples:

var minimumDelta = 0.1M;
var minLetter = "a";
var minimumMultiplier = 2;

var delta = 0.001M;
var multiplier = 1;
var value = "Z";

// Default comparer, message and exception. Throws ArgumentOutOfRangeException with default
// message.
delta.RequiresGreaterThanOrEqualTo(minimumDelta);

// User specified comparer, default message and exception. Throws ArgumentOutOfRangeException 
// with default message.
value.RequiresGreaterThanOrEqualTo(minLetter, comparer: StringComparer.OrdinalIgnoreCase);

// Default comparer and exception with custom message. Throws ArgumentOutOfRangeException 
// with custom message.
delta.RequiresGreaterThanOrEqualTo(minimumDelta, customMessage: "delta must be > 0.1");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
multiplier.RequiresGreaterThanOrEqualTo(minimumMultiplier, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
multiplier.RequiresGreaterThanOrEqualTo(minimumMultiplier, "multiplier must be > 2", CustomException.GetException);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
value.RequiresGreaterThanOrEqualTo(minLetter, "value is out of range", CustomException.GetException, StringComparer.OrdinalIgnoreCase);

RequiresGreaterThanOrEqualToZero

Use RequiresGreaterThanOrEqualToZero to express a requirement that a signed numeric value must be >= zero.

By default RequiresGreaterThanOrEqualToZero will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

RequiresGreaterThanOrEqualToZero is supported for types that implement INumber<T>. Unsigned types (Byte, Char, nuint, UInt16, UInt32, UInt62 and UInt128) are supported but will never throw because all values for unsigned types will meet the requirement.

Examples:

var delta = -1;

// Throws ArgumentOutOfRangeException with default message.
delta.RequiresGreaterThanOrEqualToZero();

// Throws ArgumentOutOfRangeException with custom message.
delta.RequiresGreaterThanOrEqualToZero("delta must be >= zero");

var sum = Half.MinValue;

// Throws CustomException with default message.
sum.RequiresGreaterThanOrEqualToZero(getException: CustomException.GetException);

// Throws CustomException with custom message.
sum.RequiresGreaterThanOrEqualToZero("sum must be >= 0", CustomException.GetException);

RequiresLessThan

Use RequiresLessThan to express a requirement that a numeric value must be < an upper limit.

By default RequiresLessThan will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "LimitExpression" and "LimitValue".

RequiresLessThan has an optional comparer parameter that allows you to provide an IComparer<T> to use when comparing the value to the lower bound. If not supplied then the comparer parameter defaults to Comparer<T>.Default.

Examples:

var maximumDelta = 0.1M;
var maxLetter = "Z";
var maximumMultiplier = 2;

var delta = 1.001M;
var multiplier = 10;
var value = "a";

// Default comparer, message and exception. Throws ArgumentOutOfRangeException with default
// message.
delta.RequiresLessThan(maximumDelta);

// User specified comparer, default message and exception. Throws ArgumentOutOfRangeException 
// with default message.
value.RequiresLessThan(maxLetter, comparer: StringComparer.OrdinalIgnoreCase);

// Default comparer and exception with custom message. Throws ArgumentOutOfRangeException 
// with custom message.
delta.RequiresLessThan(maximumDelta, customMessage: "delta must be > 0.1");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
multiplier.RequiresLessThan(maximumMultiplier, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
multiplier.RequiresLessThan(maximumMultiplier, "multiplier must be > 2", CustomException.GetException);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
value.RequiresLessThan(maxLetter, "value is out of range", CustomException.GetException, StringComparer.OrdinalIgnoreCase);

RequiresLessThanZero

Use RequiresLessThanZero to express a requirement that a signed numeric value must be < zero.

By default RequiresLessThanZero will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

RequiresLessThanZero is supported for types that implement INumber<T>. Unsigned types (Byte, Char, nuint, UInt16, UInt32, UInt62 and UInt128) are supported but will always throw because all values for unsigned types will fail the requirement.

Examples:

var deceleration = 0F;

// Throws ArgumentOutOfRangeException with default message.
deceleration.RequiresLessThanZero();

// Throws ArgumentOutOfRangeException with custom message.
deceleration.RequiresLessThanZero("deceleration must be < zero");

var rateOfChange = 50;

// Throws CustomException with default message.
rateOfChange.RequiresLessThanZero(getException: CustomException.GetException);

// Throws CustomException with custom message.
rateOfChange.RequiresLessThanZero("rateOfChange must be < 0", CustomException.GetException);

RequiresLessThanOrEqualTo

Use RequiresLessThanOrEqualTo to express a requirement that a numeric value must be ⇐ an upper limit.

By default RequiresLessThanOrEqualTo will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "LimitExpression" and "LimitValue".

RequiresLessThanOrEqualTo has an optional comparer parameter that allows you to provide an IComparer<T> to use when comparing the value to the lower bound. If not supplied then the comparer parameter defaults to Comparer<T>.Default.

Examples:

var maximumDelta = 0.1M;
var maxLetter = "Z";
var maximumMultiplier = 2;

var delta = 1.001M;
var multiplier = 10;
var value = "a";

// Default comparer, message and exception. Throws ArgumentOutOfRangeException with default
// message.
delta.RequiresLessThanOrEqualTo(maximumDelta);

// User specified comparer, default message and exception. Throws ArgumentOutOfRangeException 
// with default message.
value.RequiresLessThanOrEqualTo(maxLetter, comparer: StringComparer.OrdinalIgnoreCase);

// Default comparer and exception with custom message. Throws ArgumentOutOfRangeException 
// with custom message.
delta.RequiresLessThanOrEqualTo(maximumDelta, customMessage: "delta must be > 0.1");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
multiplier.RequiresLessThanOrEqualTo(maximumMultiplier, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
multiplier.RequiresLessThanOrEqualTo(maximumMultiplier, "multiplier must be > 2", CustomException.GetException);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
value.RequiresLessThanOrEqualTo(maxLetter, "value is out of range", CustomException.GetException, StringComparer.OrdinalIgnoreCase);

RequiresLessThanOrEqualToZero

Use RequiresLessThanOrEqualToZero to express a requirement that a signed numeric value must be >= zero.

By default RequiresLessThanOrEqualToZero will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

RequiresLessThanOrEqualToZero is supported for types that implement INumber<T>.

Examples:

var increment = 1;

// Throws ArgumentOutOfRangeException with default message.
increment.RequiresLessThanOrEqualToZero();

// Throws ArgumentOutOfRangeException with custom message.
increment.RequiresLessThanOrEqualToZero("increment must be <= zero");

var average = 0.001M;

// Throws CustomException with default message.
average.RequiresLessThanOrEqualToZero(getException: CustomException.GetException);

// Throws CustomException with custom message.
average.RequiresLessThanOrEqualToZero("average must be <= 0", CustomException.GetException);

RequiresValueBetween

Use RequiresValueBeween to express a requirement that a value must be >= a lower bound and ⇐ an upper bound. RequiresValueBetween will throw an ArgumentException if the lowerBound parameter is greater than the upperBound parameter.

By default RequiresValueBeween will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain seven entries with keys "Description", "ParamName", "ParamValue", "LowerBoundExpression", "LowerBoundValue", "UpperBoundExpression" and "UpperBoundValue".

RequiresValueBeween has an optional comparer parameter that allows you to provide an IComparer<T> to use when comparing the value to the lower bound. If not supplied then the comparer parameter defaults to Comparer<T>.Default.

var minimumDelta = 0.1M;
var maximumDelta = 0.99M;
var minLetter = "a";
var maxLetter = "z";
var minimumMultiplier = 2;
var maximumMultiplier = 64;

var delta = 0.001M;
var multiplier = 1;
var value = "Z";

// Default comparer, message and exception. Throws ArgumentOutOfRangeException with default
// message.
delta.RequiresValueBetween(minimumDelta, maximumDelta);

// User specified comparer, default message and exception. Throws ArgumentOutOfRangeException 
// with default message.
value.RequiresValueBetween(minLetter, maxLetter, comparer: StringComparer.CurrentCulture);

// Default comparer and exception with custom message. Throws ArgumentOutOfRangeException 
// with custom message.
delta.RequiresValueBetween(minimumDelta, maximumDelta, customMessage: "delta must be >= 0.1 and <= 0.99");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
multiplier.RequiresValueBetween(minimumMultiplier, maximumMultiplier, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
multiplier.RequiresValueBetween(minimumMultiplier, maximumMultiplier, "multiplier must be >= 2 and <= 64", CustomException.GetException);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
value.RequiresValueBetween(minLetter, maxLetter, "value is out of range", CustomException.GetException, StringComparer.InvariantCulture);

Equality Requirements

RequiresEqual

Use RequiresEqual to express a requirement that a value must be exactly equal to a target value. (For floating point types such as Double or Single, RequiresApproximatelyEqual is preferred.)

By default RequiresEqual will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "TargetExpression" and "TargetValue".

RequiresEqual has an optional comparer parameter that allows you to provide an IEqualityComparer<T> to use when comparing the value to the target. If not supplied then the comparer parameter defaults to EqualityComparer<T>.Default.

Examples:

var value = 17;
var target = 42;

// Default comparer, message and exception. Throws ArgumentException with default
// message.
value.RequiresEqual(target);

// Default comparer and exception with custom message. Throws ArgumentException 
// with custom message.
value.RequiresEqual(target, "incorrect value");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
value.RequiresEqual(target, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
value.RequiresEqual(target, "incorrect value", CustomException.GetException);

var str = "abc";
var targetStr = "ABC";

// User specified comparer, default message and exception. Throws ArgumentException 
// with default message.
str.RequiresEqual(targetStr, comparer: StringComparer.CurrentCulture);

// User specified comparer, custom message and default exception. Throws ArgumentException 
// with custom message.
str.RequiresEqual(targetStr, "incorrect value", comparer: StringComparer.CurrentCulture);

// User specified comparer, default message and custom exception. Throws CustomException 
// with default message.
str.RequiresEqual(targetStr, getException: CustomException.GetException, comparer: StringComparer.InvariantCulture);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
str.RequiresEqual(targetStr, "incorrect value", CustomException.GetException, StringComparer.InvariantCulture);

RequiresNotEqual

Use RequiresNotEqual to express a requirement that a value must not be exactly equal to a target value.

By default RequiresNotEqual will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "TargetExpression" and "TargetValue".

RequiresNotEqual has an optional comparer parameter that allows you to provide an IEqualityComparer<T> to use when comparing the value to the target. If not supplied then the comparer parameter defaults to EqualityComparer<T>.Default.

Examples:

var value = 1123581321;
var target = 1123581321;

// Default comparer, message and exception. Throws ArgumentException with default
// message.
value.RequiresNotEqual(target);

// Default comparer and exception with custom message. Throws ArgumentException 
// with custom message.
value.RequiresNotEqual(target, "can't be same value");

// Default comparer and message, custom exception. Throws CustomException with default 
// message.
value.RequiresNotEqual(target, getException: CustomException.GetException);

// Default comparer, custom message and exception. Throws CustomException with custom 
// message.
value.RequiresNotEqual(target, "can't be same value", CustomException.GetException);

var str = "abc";
var targetStr = "ABC";

// User specified comparer, default message and exception. Throws ArgumentException 
// with default message.
str.RequiresNotEqual(targetStr, comparer: StringComparer.CurrentCultureIgnoreCase);

// User specified comparer, custom message and default exception. Throws ArgumentException 
// with custom message.
str.RequiresNotEqual(targetStr, "can't be same value", comparer: StringComparer.CurrentCultureIgnoreCase);

// User specified comparer, default message and custom exception. Throws CustomException 
// with default message.
str.RequiresNotEqual(targetStr, getException: CustomException.GetException, comparer: StringComparer.InvariantCultureIgnoreCase);

// User specified comparer, custom message and custom exception. Throws CustomException 
// with custom message.
str.RequiresNotEqual(targetStr, "can't be same value", CustomException.GetException, StringComparer.InvariantCultureIgnoreCase);

RequiresApproximatelyEqual

Use RequiresApproximatelyEqual to express a requirement that a value must be no more than +/- a tolerance value from target value. The check is performed as ABS(value - target) ⇐ tolerance.

RequiresApproximatelyEqual is supported for types that implement IFloatingPoint<T> and should be used instead of RequiresEqual for those types.

RequiresApproximatelyEqual will throw an ArgumentOutOfRangeException if the tolerance is ⇐ zero.

By default RequiresApproximatelyEqual will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain seven entries with keys "Description", "ParamName", "ParamValue", "TargetExpression", "TargetValue", "ToleranceExpression" and "ToleranceValue".

Examples:

var value = 42.42;
var target = 42.35;
var tolerance = 0.001;

// Throws ArgumentException with default message.
value.RequiresApproximatelyEqual(target, tolerance);

// Throws ArgumentException with custom message.
value.RequiresApproximatelyEqual(target, tolerance, "value not within tolerance");

// Throws CustomException with default message.
value.RequiresApproximatelyEqual(target, tolerance, getException: CustomException.GetException);

// Throws CustomException with custom message.
value.RequiresApproximatelyEqual(target, tolerance, "value not within tolerance", CustomException.GetException);

Enum Requirements

RequiresEnumIsDefined

Use RequiresEnumIsDefined to express a requirement that a value be a defined member of an enum type.

By default RequiresEnumIsDefined will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "ParamValue" and "TypeName".

Examples:

public enum AddressType
{
    Home,
    Mailing,
    Billing,
    Shipping
}

var addressType = (AddressType)99;

// Throws ArgumentOutOfRangeException with default message.
addressType.RequiresEnumIsDefined();

// Throws ArgumentOutOfRangeException with custom message.
addressType.RequiresEnumIsDefined("unrecognized address type");

// Throws CustomException with default message.
addressType.RequiresEnumIsDefined(getException: CustomException.GetException);

// Throws CustomException with custom message.
addressType.RequiresEnumIsDefined("unrecognized address type", CustomException.GetException);

RequiresEnumIsDefinedAndNotDefault

Use RequiresEnumIsDefinedAndNotDefault to express a requirement that a value be a defined member of an enum type but not the default value for the enum type. This is useful when defining requirements on an object that was deserialized from JSON. Consider the following:

public enum Casing
{
	None = 0,
	CamelCase,
	PascalCase,
	SnakeCase,
	KebabCase,
};

public class GenerationOptions
{
	public Casing ClassCasing { get; set; }
	public Casing MethodCasing { get; set; }
}

If the string '{ MethodCasing: "PascalCase" }' is deserialized into an object of type GenerationOptions, the ClassCasing property would be Casing.None. In a REST API it is difficult to determine if the intent was truly to set ClassCasing to Casing.None or if the ClassCasing property being left off was an oversight. Our preference is to define all enums with a 'None' item but not allow 'None' to have meaning in an API. Then RequiresEnumIsDefinedAndNotDefault can be used when coding guard clauses.

By default RequiresEnumIsDefinedAndNotDefault will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "ParamValue" and "TypeName".

Examples:

public enum Priority
{
    None = 0,
    First,
    Second,
    Third
}

var priority = Priority.None;

// Throws ArgumentOutOfRangeException with default message.
priority.RequiresEnumIsDefinedAndNotDefault();

// Throws ArgumentOutOfRangeException with custom message.
priority.RequiresEnumIsDefinedAndNotDefault("priority is not valid");

// Throws CustomException with default message.
priority.RequiresEnumIsDefinedAndNotDefault(getException: CustomException.GetException);

// Throws CustomException with custom message.
priority.RequiresEnumIsDefinedAndNotDefault("priority is not valid", CustomException.GetException);

RequiresHasFlag

Use RequiresHasFlag to express a requirement that an enum value should have one or more flags set.

By default RequiresHasFlag will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue", "TypeName" and "Flags".

The enum type being tested should have the [Flags] attribute. If the flags parameter is zero (i.e. no flags set) RequiresHasFlag will not throw.

Examples:

[Flags]
public enum CLanguageFamily
{
   None = 0,
   BCPL = 1,
   B = 2,
   C = 4,
   CPlusPlus = 8,
   Java = 16,
   JavaScript = 32,
   CSharp = 64,
   Go = 128,
   Dart = 256,
   Rust = 512
}

var skills = CLanguageFamily.C | CLanguageFamily.CPlusPlus | CLanguageFamily.Java;
var jobRequirements = CLanguageFamily.Dart;

// Throws ArgumentException with default message.
skills.RequiresHasFlag(jobRequirements);

// Throws ArgumentException with custom message.
skills.RequiresHasFlag(jobRequirements, "applicant does not have required skill(s)");

// Throws CustomException with default message.
skills.RequiresHasFlag(jobRequirements, getException: CustomException.GetException);

// Throws CustomException with custom message.
skills.RequiresHasFlag(jobRequirements, "applicant does not have required skill(s)", CustomException.GetException);

String Requirements

RequiresAlphaNumericOnly

Use RequiresAlphaNumericOnly to express a requirement that a string value must contain only letter or digit characters. An empty value will fail the requirement and will throw an exception.

RequiresAlphaNumericOnly will throw an ArgumentNullException if the string value is null.

By default RequiresAlphaNumericOnly will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var value = "y = x + 1";

// Throws ArgumentException with default message.
value.RequiresAlphaNumericOnly();

// Throws ArgumentException with custom message.
value.RequiresAlphaNumericOnly("must contain only letters and digits");

// Throws CustomException with default message.
value.RequiresAlphaNumericOnly(getException: CustomException.GetException);

// Throws CustomException with custom message.
value.RequiresAlphaNumericOnly("must contain only letters and digits", CustomException.GetException);

RequiresContains

Use RequiresContains to express a requirement that a string value must contain a specific substring.

RequiresContains will throw an ArgumentNullException if the string value is null. It will throw an ArgumentOutOfRangeException if the contained string parameter is null or empty. RequiresContains also has an optional comparisonType parameter, which is an enum of type StringComparison. If the comparisonType is not a valid member of StringComparison then RequiresContain will throw an ArgumentOutOfRangeException.

By default RequiresContains will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue" "TargetExpression" and "TargetValue".

Examples:

var str = "This is a test. This is only a test. For the next sixty seconds...";
var target = "ONLY A TEST";

// Default comparison, message and exception. Throws an ArgumentException with
// default message.
str.RequiresContains(target);

// User specified comparison, default message and exception. Throws an 
// ArgumentException with default message.
str.RequiresContains(target, comparisonType: StringComparison.InvariantCulture);

// Default comparison and exception with custom message. Throws ArgumentException 
// with custom message.
str.RequiresContains(target, "does not contain expected substring");

// Default comparison and message, custom exception. Throws CustomException with  
// default message.
str.RequiresContains(target, getException: CustomException.GetException);

// Default comparison, custom message and exception. Throws CustomException with custom 
// message.
str.RequiresContains(target, "does not contain expected substring", CustomException.GetException);

// User specified comparison, custom message and custom exception. Throws CustomException 
// with custom message.
str.RequiresContains(target, "does not contain expected substring", CustomException.GetException, StringComparison.CurrentCulture);

RequiresDigitsOnly

Use RequiresDigitsOnly to express a requirement that a string value must contain only digit characters. An empty value will fail the requirement and will throw an exception.

RequiresDigitsOnly will throw an ArgumentNullException if the string value is null.

By default RequiresDigitsOnly will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var value = "123Abc456";

// Throws ArgumentException with default message.
value.RequiresDigitsOnly();

// Throws ArgumentException with custom message.
value.RequiresDigitsOnly("must contain only digits");

// Throws CustomException with default message.
value.RequiresDigitsOnly(getException: CustomException.GetException);

// Throws CustomException with custom message.
value.RequiresDigitsOnly("must contain only digits", CustomException.GetException);

RequiresMaxLength

Use RequiresMaxLength to express a maximum length requirement for a string value. RequiresMaxLength treats a null string as a zero length string.

RequiresMaxLength will throw an ArgumentOutOfRange exception if the maxLength parameter is a negative number.

By default RequiresMaxLength will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue" "MinLengthExpression" and "MinLengthValue".

Examples:

var maxLength = 20;

var str = "This is a test. This is only a test. For the next sixty seconds...";

// Throws ArgumentException with default message.
str.RequiresMaxLength(maxLength);

// Throws ArgumentException with custom message.
str.RequiresMaxLength(maxLength, "maximum length allowed is 20 characters");

// Throws CustomException with default message.
str.RequiresMaxLength(maxLength, getException: CustomException.GetException);

// Throws CustomException with custom message.
str.RequiresMaxLength(maxLength, "maximum length allowed is 20 characters", CustomException.GetException);

RequiresMinLength

Use RequiresMinLength to express a maximum length requirement for a string value. RequiresMinLength treats a null string as a zero length string.

RequiresMinLength will throw an ArgumentOutOfRange exception if the minLength parameter is a negative number.

By default RequiresMinLength will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue" "MinLengthExpression" and "MinLengthValue".

Examples:

var minLength = 20;

var str = "abc";

// Throws ArgumentException with default message.
str.RequiresMinLength(minLength);

// Throws ArgumentException with custom message.
str.RequiresMinLength(minLength, "minimum length allowed is 20 characters");

// Throws CustomException with default message.
str.RequiresMinLength(minLength, getException: CustomException.GetException);

// Throws CustomException with custom message.
str.RequiresMinLength(minLength, "minimum length allowed is 20 characters", CustomException.GetException);

RequiresNotEmptyOrWhiteSpace

Use RequiresNotEmptyOrWhiteSpace for cases where a null string is valid, but if the string is not null then it may not be String.Empty or all whitespace characters.

By default RequiresNotEmptyOrWhiteSpace will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var str = "\t";

// Throws ArgumentException with default message.
str.RequiresNotEmptyOrWhiteSpace();

// Throws ArgumentException with custom message.
str.RequiresNotEmptyOrWhiteSpace("value my not be empty or all whitespace characters");

// Throws CustomException with default message.
str.RequiresNotEmptyOrWhiteSpace(getException: CustomException.GetException);

// Throws CustomException with custom message.
str.RequiresNotEmptyOrWhiteSpace("value my not be empty or all whitespace characters", CustomException.GetException);

RequiresNotNullOrEmpty

Use RequiresNotNullOrEmpty to express a requirement that a value not be null or String.Empty (""). Internally, RequiresNotNullOrEmpty uses the string static method IsNullOrEmpty.

By default RequiresNotNullOrEmpty will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var str = "";

// Throws ArgumentException with default message.
str.RequiresNotNullOrEmpty();

// Throws ArgumentException with custom message.
str.RequiresNotNullOrEmpty("value my not be null or empty");

// Throws CustomException with default message.
str.RequiresNotNullOrEmpty(getException: CustomException.GetException);

// Throws CustomException with custom message.
str.RequiresNotNullOrEmpty("value my not be null or empty", CustomException.GetException);

RequiresNotNullOrWhiteSpace

Use RequiresNotNullOrWhiteSpace to express a requirement that a value not be null, String.Empty ("") or all whitespace characters. Internally, RequiresNotNullOrWhiteSpace uses the string static method IsNullOrWhiteSpace.

By default RequiresNotNullOrWhiteSpace will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "ParamValue".

Examples:

var str = "";

// Throws ArgumentException with default message.
str.RequiresNotNullOrWhiteSpace();

// Throws ArgumentException with custom message.
str.RequiresNotNullOrWhiteSpace("value my not be null, empty or all whitespace characters");

// Throws CustomException with default message.
str.RequiresNotNullOrWhiteSpace(getException: CustomException.GetException);

// Throws CustomException with custom message.
str.RequiresNotNullOrWhiteSpace("value my not be null, empty or all whitespace characters", CustomException.GetException);

RequiresRegexMatch

Use RequiresRegexMatch to express requirements that a string value should match a regular expression. RequiresRegexMatch has overloads that accept a string regular expression or a pre-built Regex object. If the string value being checked is null then RequiresRegexMatch will throw an ArgumentNullException. If the string regular expression is null, String.Empty or all whitespace characters then RequiresRegexMatch will throw an ArgumentException. If the pre-built regex is null then RequiresRegexMatch will throw an ArgumentNullException.

By default RequiresRegexMatch will throw an ArgumentException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "ParamValue" and "Regex".

Examples:

var emailAddressRegex = @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$";

var emailAddress = "abc@xyz.";

// Throws ArgumentException with default message.
emailAddress.RequiresRegexMatch(emailAddressRegex);

// Throws ArgumentException with custom message.
emailAddress.RequiresRegexMatch(emailAddressRegex, customMessage: "invalid email address format");

// Throws CustomException with default message.
emailAddress.RequiresRegexMatch(emailAddressRegex, getException: CustomException.GetException);

// Throws CustomException with custom message.
emailAddress.RequiresRegexMatch(emailAddressRegex, customMessage: "invalid email address format", getException: CustomException.GetException);


var ipAddressRegex = new Regex(@"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");

var ipAddress = "1.2.3.abc";

// Throws ArgumentException with default message.
ipAddress.RequiresRegexMatch(ipAddressRegex);

// Throws ArgumentException with custom message.
ipAddress.RequiresRegexMatch(ipAddressRegex, customMessage: "invalid IP address");

// Throws CustomException with default message.
ipAddress.RequiresRegexMatch(ipAddressRegex, getException: CustomException.GetException);

// Throws CustomException with custom message.
ipAddress.RequiresRegexMatch(ipAddressRegex, customMessage: "invalid IP address", getException: CustomException.GetException);

RequiresStartsWith

Use RequiresStartsWith to express a requirement that a string value must start with a specific substring.

RequiresStartsWith will throw an ArgumentNullException if the string value is null. It will throw an ArgumentOutOfRangeException if the start string parameter is null or empty. RequiresStartsWith also has an optional comparisonType parameter, which is an enum of type StringComparison. If the comparisonType is not a valid member of StringComparison then RequiresStartsWith will throw an ArgumentOutOfRangeException.

By default RequiresStartsWith will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "ParamValue" "TargetExpression" and "TargetValue".

Examples:

var str = "This is a test. This is only a test. For the next sixty seconds...";
var target = "THIS IS A TEST";

// Default comparison, message and exception. Throws an ArgumentException with
// default message.
str.RequiresStartsWith(target);

// User specified comparison, default message and exception. Throws an 
// ArgumentException with default message.
str.RequiresStartsWith(target, comparisonType: StringComparison.InvariantCulture);

// Default comparison and exception with custom message. Throws ArgumentException 
// with custom message.
str.RequiresStartsWith(target, "does not start with expected substring");

// Default comparison and message, custom exception. Throws CustomException with  
// default message.
str.RequiresContains(target, getException: CustomException.GetException);

// Default comparison, custom message and exception. Throws CustomException with custom 
// message.
str.RequiresStartsWith(target, "does not start with expected substring", CustomException.GetException);

// User specified comparison, custom message and custom exception. Throws CustomException 
// with custom message.
str.RequiresStartsWith(target, "does not start with expected substring", CustomException.GetException, StringComparison.CurrentCulture);

Collection Requirements

Collection requirements are implemented for IReadOnlyCollection<T> to support the widest range of types. Though you can invoke a collection requirement for any type that implements IReadOnlyCollection<T>, the return value will be IReadOnlyCollection<T> and not the original type, potentially requiring a cast if the requirement is used in an assignment statement in an object constructor. To partially alleviate this issue, the full set of collection requirements is implemented for List<T> and System.Array<T> as wrapper methods around the IReadOnlyCollection<T> versions that return List<T> or System.Array<T>. Other collection types may be included in future releases if needed.

Note that collection requirements can partially or fully enumerate the collection and chained requirements may end up enumerating the collection multiple times before you even get to the work of the method containing the requirement. Depending on your specific use case, it may be better to apply requirements to the individual items in the collection as you process them and use a custom exception factory to generate an exception other than the default argument exception rather than making multiple passes through the input collection just to verify its correctness.

Collection requirements are specifically not implemented for IEnumerable<T> because of the potential for duplicate enumeration of LINQ queries and the issues that are introduced.

RequiresAll

Use RequiresAll to express a requirement that every item in a collection must meet a condition.

RequiresAll uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresAll will throw an ArgumentNullException.

By default RequiresAll will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "Condition", "Index" and "FailingItem".

Also see RequiresNone. RequiresAll is the equivalent of RequiresNone with a negative condition. Using RequiresAll instead of RequiresNone may allow you to avoid the confusion of a requirement that is stated using a negative condition.

Examples:

var collection = new List<Double> { Double.MinValue, Double.MaxValue, Double.PositiveInfinity };

// Throws ArgumentException with default message.
collection.RequiresAll(x => !Double.IsInfinity(x));

// Throws ArgumentException with custom message.
collection.RequiresAll(x => !Double.IsInfinity(x), "infinity is not a valid value");

// Throws CustomException with default message.
collection.RequiresAll(x => !Double.IsInfinity(x), getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresAll(x => !Double.IsInfinity(x), "infinity is not a valid value", CustomException.GetException);

RequiresAny

Use RequiresAny to express a requirement that at least one item in a collection must meet a condition.

RequiresAny uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresAny will throw an ArgumentNullException.

By default RequiresAny will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "Condition".

Examples:

var addresses = new List<Address>()
{
    new Address(AddressType.Shipping, "Address Line 1", null, "City", "ST", "00000"),
    new Address(AddressType.Billing, "Address Line 1", "Attn: Billing Dept", "City", "ST", "00000"),
};

// Throws ArgumentException with default message.
addresses.RequiresAny(x => x.AddressType == AddressType.Billing);

// Throws ArgumentException with custom message.
addresses.RequiresAny(x => x.AddressType == AddressType.Billing, "Billing address is required");

// Throws CustomException with default message.
addresses.RequiresAny(x => x.AddressType == AddressType.Billing, getException: CustomException.GetException);

// Throws CustomException with custom message.
addresses.RequiresAny(x => x.AddressType == AddressType.Billing, "Billing address is required", CustomException.GetException);

RequiresAtLeast

Use RequiresAtLeast to express a requirement that at least N items in a collection must meet a condition. N must be greater than or equal to one (1), otherwise RequiresAtLeast will throw an ArgumentOutOfRangeException.

RequiresAtLeast uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresAtLeast will throw an ArgumentNullException.

By default RequiresAtLeast will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "Condition", "CountExpression" and "CountValue".

Examples:

var minimumDuration = 100;
var requiredDurations = 5;

var durations = new List<Int32> { 201, 386, 95, 217, 455, 382, 42 };

// Throws ArgumentException with default message.
durations.RequiresAtLeast(x => x > minimumDuration, requiredDurations);

// Throws ArgumentException with custom message.
durations.RequiresAtLeast(x => x > minimumDuration, requiredDurations, "Insufficient durations to perform analysis");

// Throws CustomException with default message.
durations.RequiresAtLeast(x => x > minimumDuration, requiredDurations, getException: CustomException.GetException);

// Throws CustomException with custom message.
durations.RequiresAtLeast(x => x > minimumDuration, requiredDurations, "Insufficient durations to perform analysis", CustomException.GetException);

RequiresAtMost

Use RequiresAtMost to express a requirement that at most N items in a collection can meet a condition. N must be greater than or equal to one (1), otherwise RequiresAtMost will throw an ArgumentOutOfRangeException.

RequiresAtMost uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresAtMost will throw an ArgumentNullException.

By default RequiresAtMost will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "Condition", "CountExpression" and "CountValue".

Examples:

var collection = new List<String> { "Tango", "Tea", "Two", "Twain" };
var startsWithT = (String x) => x.StartsWith('T');
var maxCount = 3;

// Throws ArgumentException with default message.
collection.RequiresAtMost(startsWithT, maxCount);

// Throws ArgumentException with custom message.
collection.RequiresAtMost(startsWithT, maxCount, "too many T's!");

// Throws CustomException with default message.
collection.RequiresAtMost(startsWithT, maxCount, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresAtMost(startsWithT, maxCount, "too many T's!", CustomException.GetException);

RequiresAtMostOne

Use RequiresAtMostOne to express a requirement that at most one (1) item in a collection can meet a condition. Note that there is no corresponding RequiresAtLeastOne method because RequiresAny serves the same purpose.

RequiresAtMostOne uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresAtMostOne will throw an ArgumentNullException.

By default RequiresAtMostOne will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "Condition".

Examples:

var collection = new List<String> { "Zebra", "Zachary", "Yam", "Xylophone" };
var startsWithZ = (String x) => x.StartsWith('Z');

// Throws ArgumentException with default message.
collection.RequiresAtMostOne(startsWithZ);

// Throws ArgumentException with custom message.
collection.RequiresAtMostOne(startsWithZ, "only one 'Z' allowed");

// Throws CustomException with default message.
collection.RequiresAtMostOne(startsWithZ , getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresAtMostOne(startsWithZ, "only one 'Z' allowed", CustomException.GetException);

RequiresCount

Use RequiresCount to express a requirement that a collection must contain exactly N items.

RequiresCount will throw an ArgumentOutOfRange exception if count is negative.

By default RequiresCount will throw an ArgumentException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "CountExpression" and "CountValue".

Examples:

var count = 10;
var collection = Enumerable.Range(0, count - 1).ToList();

// Throws ArgumentException with default message.
collection.RequiresCount(count);

// Throws ArgumentException with custom message.
collection.RequiresCount(count, "requires 10 items");

// Throws CustomException with default message.
collection.RequiresCount(count, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresCount(count, "requires 10 items", CustomException.GetException);

RequiresDistinct

Use RequiresDistinct to express a requirement that all of the items in the collection must be unique.

RequiresDistinct has an optional comparer parameter that allows you to provide an IEqualityComparer<T> to use when comparing the items in the collection. If the IEqualityComparer<T> is null then EqualityComparer<T>.Default is used as the comparer.

RequiresDistinct also has an overload that allows you to supply a selector function of type Func<T, S> that allows you to select properties of the items in the collection to check for distinctness. If the selector parameter is null then RequiresDistinct will throw an ArgumentNullException.

By default RequiresDistinct will throw an ArgumentException. The data collection supplied to the getException method will contain two (or three) entries with keys "Description" and "ParamName" (and "Selector" for the selector overload).

Examples:

var incidentDates = new List<DateOnly> { DateOnly.Parse("1/1/2021"), DateOnly.Parse("2/2/2022"), DateOnly.Parse("1/1/2021") };

// Throws ArgumentException with default message.
incidentDates.RequiresDistinct();

// Throws ArgumentException with custom message.
incidentDates.RequiresDistinct(customMessage: "incident dates must be unique");

var letters = new List<String> { "A", "B", "C", "a", "b", "c" };

// Throws CustomException with default message.
letters.RequiresDistinct(getException: CustomException.GetException, comparer: StringComparer.OrdinalIgnoreCase);

// Throws CustomException with custom message.
letters.RequiresDistinct("letters must be unique", CustomException.GetException, StringComparer.CurrentCultureIgnoreCase);

var addresses = new List<Address>()
{
    new Address(AddressType.Shipping, "Address Line 1", null, "City", "ST", "00000"),
    new Address(AddressType.Billing, "Address Line 1", "Attn: Billing Dept", "City", "ST", "00000"),
    new Address(AddressType.Shipping, "Second Address", null, "SecondCity", "ST", "00000"),
};

// Throws ArgumentException with default message.
addresses.RequiresDistinct(x => x.AddressType);

// Throws ArgumentException with custom message.
addresses.RequiresDistinct(x => x.AddressType, customMessage: "Address types must be unique");

var names = new List<String> { "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Able", "baker" };

// Throws CustomException with default message.
names.RequiresDistinct(x => x[..1], getException: CustomException.GetException, comparer: StringComparer.OrdinalIgnoreCase);

// Throws CustomException with custom message.
names.RequiresDistinct(x => x[..1], "first letters must be unique", CustomException.GetException, StringComparer.InvariantCulture);

RequiresDoesNotIntersect

Use RequiresDoesNotIntersect to express a requirement that a collection may not contain any items in common with another excludes collection.

The excludes collection must be non-empty. RequiresDoesNotIntersect will throw an ArgumentNullException if the excludes collection is null. It will throw an ArgumentException if the excludes collection is empty.

RequiresDoesNotIntersect has an optional comparer parameter that allows you to provide an IEqualityComparer<T> to use when comparing the items in the collection and the target excludes. If the IEqualityComparer<T> is null then EqualityComparer<T>.Default is used as the comparer.

By default RequiresDoesNotIntersect will throw an ArgumentException. The data collection supplied to the getException method will contain 3 entries with keys "Description", "ParamName" and "ExcludesExpression".

Examples:

// Collective nouns for animals
var collection = new String[] { "swarm", "shoal", "herd", "fleet", "flock", "pride", "pack", "colony", "murder" };
// Collective nouns for people.
var excludes = new String[] { "choir", "troop", "band", "board", "fleet", "team", "pack", "delegation", "bench" };

// Throws ArgumentException with default message.
collection.RequiresDoesNotIntersect(excludes);

// Throws ArgumentException with custom message.
collection.RequiresDoesNotIntersect(excludes, "requires no items in common");

// Throws CustomException with default message.
collection.RequiresDoesNotIntersect(excludes, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresDoesNotIntersect(excludes, "requires no items in common", CustomException.GetException);

// Optional comparer parameter.
var upperCaseExcludes = excludes.Select(x => x.ToUpper()).ToArray();
      collection.RequiresDoesNotIntersect(upperCaseExcludes, comparer: StringComparer.InvariantCultureIgnoreCase);

RequiresIntersect

Use RequiresIntersect to express a requirement that a collection must contain items in common with another includes collection.

The includes collection must be non-empty. RequiresIntersect will throw an ArgumentNullException if the includes collection is null. It will throw an ArgumentException if the includes collection is empty.

The number of items in common is specified by an optional count parameter which defaults to one (1) if it is not supplied. RequiresInterset will throw an ArgumentOutOfRangeException if the count parameter is less than one (1) or if count is greater than the number of items in the collection or greater than the number of items in the includes collection.

RequiresIntersect has an optional comparer parameter that allows you to provide an IEqualityComparer<T> to use when comparing the items in the collection and the includes collection. If the IEqualityComparer<T> is null then EqualityComparer<T>.Default is used as the comparer.

By default RequiresIntersect will throw an ArgumentException. The data collection supplied to the getException method will contain 5 entries with keys "Description", "ParamName", "IncludesExpression", "CountExpression" and "CountValue".

Examples:

// Collective nouns for animals
var collection = new String[] { "swarm", "shoal", "herd", "fleet", "flock", "pride", "pack", "colony", "murder" };
// Collective nouns for people.
var includes = new String[] { "choir", "troop", "band", "board", "team", "delegation", "bench" };

// Throws ArgumentException with default message.
collection.RequiresIntersect(includes);

// Throws ArgumentException with custom message.
collection.RequiresIntersect(includes, "requires at least one item in common");

// Throws CustomException with default message.
collection.RequiresIntersect(includes, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresIntersect(includes, "requires at least one item in common", CustomException.GetException);

// Optional count parameter.
collection.RequiresIntersect(includes, count: 3);

// Optional comparer parameter.
var upperCaseIncludes = includes.Select(x => x.ToUpper()).ToArray();
collection.RequiresIntersect(upperCaseIncludes, comparer: StringComparer.InvariantCultureIgnoreCase);

RequiresMaxCount

Use RequiresMaxCount to express a requirement that a collection must contain a maximum of N items.

RequiresMaxCount will throw an ArgumentOutOfRange exception if the minimum count is negative.

By default RequiresMaxCount will throw an ArgumentException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "CountExpression" and "CountValue".

Examples:

var maxCount = 10;
var collection = Enumerable.Range(0, maxCount + 1).Select(x => Guid.NewGuid()).ToHashSet();

// Throws ArgumentException with default message.
collection.RequiresMaxCount(maxCount);

// Throws ArgumentException with custom message.
collection.RequiresMaxCount(maxCount, "requires maximum 10 items");

// Throws CustomException with default message.
collection.RequiresMaxCount(maxCount, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresMaxCount(maxCount, "requires maximum 10 items", CustomException.GetException);

RequiresMinCount

Use RequiresMinCount to express a requirement that a collection must contain at least N items.

RequiresMinCount will throw an ArgumentOutOfRange exception if the minimum count is negative.

By default RequiresMinCount will throw an ArgumentException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "CountExpression" and "CountValue".

Examples:

var minCount = 10;
var collection = Enumerable.Range(0, minCount - 1).Select(x => (Char)('a' + x)).ToArray();

// Throws ArgumentException with default message.
collection.RequiresMinCount(minCount);

// Throws ArgumentException with custom message.
collection.RequiresMinCount(minCount, "requires at least 10 items");

// Throws CustomException with default message.
collection.RequiresMinCount(minCount, getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresMinCount(minCount, "requires at least 10 items", CustomException.GetException);

RequiresNone

Use RequiresNone to express a requirement that none of the items in a collection may meet a condition.

RequiresNone uses a condition parameter of type Func<T, Boolean> that is applied to the items in the collection when determining if the collection meets the requirement. If the condition parameter is null RequiresNone will throw an ArgumentNullException.

By default RequiresNone will throw an ArgumentException. The data collection supplied to the getException method will contain five entries with keys "Description", "ParamName", "Condition", "Index" and "FailingItem".

Also see RequiresAll. RequiresNone is the equivalent of RequiresAll with a negative condition. Using RequireNone instead of RequiresAll may allow you to avoid the confusion of a requirement that is stated using a negative condition.

Examples:

var collection = new List<Double> { Double.MinValue, Double.MaxValue, Double.PositiveInfinity };

// Throws ArgumentException with default message.
collection.RequiresNone(x => Double.IsInfinity(x));

// Throws ArgumentException with custom message.
collection.RequiresNone(x => Double.IsInfinity(x), "infinity is not a valid value");

// Throws CustomException with default message.
collection.RequiresNone(x => Double.IsInfinity(x), getException: CustomException.GetException);

// Throws CustomException with custom message.
collection.RequiresNone(x => Double.IsInfinity(x), "infinity is not a valid value", CustomException.GetException);

RequiresNotEmpty

Use RequiresNotEmpty to express a requirement that the collection must contain at least one item.

By default RequiresNotEmpty will throw an ArgumentException. The data collection supplied to the getException method will contain two entries with keys "Description" and "ParamName".

Examples:

var identifiers = new List<Guid>();

// Throws ArgumentException with default message.
identifiers.RequiresNotEmpty();

// Throws ArgumentException with custom message.
identifiers.RequiresNotEmpty("identifiers may not be empty");

// Throws CustomException with default message.
identifiers.RequiresNotEmpty(getException: CustomException.GetException);

// Throws CustomException with custom message.
identifiers.RequiresNotEmpty("identifiers may not be empty", CustomException.GetException);

Miscellaneous Requirements

RequiresConditionIsTrue

Use RequiresConditionIsTrue to express a requirement for a value that is written as a condition of type Func<T, Boolean> where T is the value type. If the condition evaluates to false when it is passed the value then a RequirementFailedException is thrown.

By default RequiresConditionIsTrue will throw an ArgumentException. The data collection supplied to the getException method will contain three entries with keys "Description", "ParamName" and "Condition".

Examples:

public enum DeliveryType
{
   Download,
   Package
}

public record Order(DeliveryType DeliveryType, Address? ShippingAddress);

var order = new Order(DeliveryType.Package, null);

var deliveryIsValid = (Order x) => 
    (x.DeliveryType == DeliveryType.Download
    || (x.DeliveryType == DeliveryType.Package && x.ShippingAddress != null));


// Throws ArgumentException with default message.
order.RequiresConditionIsTrue(deliveryIsValid);

// Throws ArgumentException with custom message.
order.RequiresConditionIsTrue(deliveryIsValid, "Shipping address is required for package delivery");

// Throws CustomException with default message.
order.RequiresConditionIsTrue(deliveryIsValid, getException: CustomException.GetException);

// Throws CustomException with custom message.
order.RequiresConditionIsTrue(deliveryIsValid, "Shipping address is required for package delivery", CustomException.GetException);

RequiresMemberOf

Use RequiresMemberOf to express a requirement that a value must be a member of a set of valid values.

RequiresMemberOf will throw an ArgumentNullException of the valid values collection is null and an ArgumentException of the valid values collection is non-null but still empty. RequiresMemberOf also has an overload that allows you to provide an IEqualityComparer<T> to use when determining if the value is a member of the valid values collection. If the IEqualityComparer<T> is null then RequiresMemberOf will throw an ArgumentNullException.

By default RequiresMemberOf will throw an ArgumentOutOfRangeException. The data collection supplied to the getException method will contain four entries with keys "Description", "ParamName", "ParamValue" and "ValidValues".

Examples:

var weekDays = new List<DayOfWeek> { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };

var day = DayOfWeek.Saturday;

// Throws ArgumentOutOfRangeException with default message.
day.RequiresMemberOf(weekDays);

// Throws ArgumentOutOfRangeException with custom message.
day.RequiresMemberOf(weekDays, "day must be Monday through Friday");

// Throws CustomException with default message.
day.RequiresMemberOf(weekDays, getException: CustomException.GetException);

// Throws CustomException with custom message.
day.RequiresMemberOf(weekDays, "day must be Monday through Friday", CustomException.GetException);

// Using IComparer<T>
var spanishWeekDays = new List<String> { "lunes", "martes", "miercoles", "jueves", "viernes" };

var textDay = "sabado";

// Throws ArgumentOutOfRangeException with default message.
textDay.RequiresMemberOf(spanishWeekDays, StringComparer.InvariantCultureIgnoreCase);

// Throws ArgumentOutOfRangeException with custom message.
textDay.RequiresMemberOf(
    spanishWeekDays, 
    StringComparer.InvariantCultureIgnoreCase, 
    "seleccione lunes, martes, miercoles, jueves o viernes");

// Throws CustomException with default message.
textDay.RequiresMemberOf(
    spanishWeekDays, 
    StringComparer.InvariantCultureIgnoreCase, 
    getException: CustomException.GetException);

// Throws CustomException with custom message.
textDay.RequiresMemberOf(
    spanishWeekDays, 
    StringComparer.InvariantCultureIgnoreCase, 
    "seleccione lunes, martes, miercoles, jueves o viernes", 
    CustomException.GetException);

Release History/Release Notes

v1.0.0-alpha

Initial limited release.

v1.0.0

Initial release.

v1.0.1

Fix minor issue where chained requirements incorrectly report the ParamName property as the entire preceding chain of requirements, instead of just the value being checked.

v2.0.0-alpha

Limited pre-release with updates for .NET 7.0 RC1.

Numeric range requirements (RequiresGreaterThanZero, RequiresGreaterThanOrEqualToZero, RequiresLessThanZero, RequiresLessThanOrEqualToZero, RequiresApproximatelyEqual) updated to use generic math support (INumber<T> and IFloatingPoint<T>).

Added collection requirements for List<T> and System.Array<T>.

Added collection requirements RequiresIntersect and RequiresDoesNotIntersect.

Exposed ExceptionFactories class for public use.

Benchmarks

Future

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0 252 11/22/2023
2.0.0 350 11/9/2022
2.0.0-alpha 167 10/23/2022
1.0.1 231 12/28/2021
1.0.0 212 12/25/2021
1.0.0-alpha 182 11/23/2021

V1.0.1 - Fix minor issue where chained requirements incorrectly report the ParamName property as the entire preceding chain of requirements, instead of just the value being checked.

V2.0.0-alpha - Update for .NET 7.0 RC1.

Numeric range requirements (RequiresGreaterThanZero, RequiresGreaterThanOrEqualToZero,
RequiresLessThanZero, RequiresLessThanOrEqualToZero, RequiresApproximatelyEqual)
updated to use generic math support (INumber<T> and IFloatingPoint<T>).