GuardRequires 2.0.0-alpha
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
<PackageReference Include="GuardRequires" Version="2.0.0-alpha" />
paket add GuardRequires --version 2.0.0-alpha
#r "nuget: GuardRequires, 2.0.0-alpha"
// 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 | Versions 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. |
-
net7.0
- System.Text.Json (>= 6.0.6)
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.1 | 90 | 11/26/2024 |
3.0.0 | 257 | 11/22/2023 |
2.0.0 | 354 | 11/9/2022 |
2.0.0-alpha | 169 | 10/23/2022 |
1.0.1 | 239 | 12/28/2021 |
1.0.0 | 218 | 12/25/2021 |
1.0.0-alpha | 184 | 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>).