GuardRequires 1.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 1.0.0-alpha
NuGet\Install-Package GuardRequires -Version 1.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="1.0.0-alpha" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add GuardRequires --version 1.0.0-alpha
#r "nuget: GuardRequires, 1.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=1.0.0-alpha&prerelease

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

Guard/Requires

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

Guards That State Requirements

Typical guard methods 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 ( x.RequiresNotNull(); ) with the inverse test performed in the RequiresNotNull method. These lets developers clearly state the requirements for a method with a block of Requires... statements at the beginning of the method and contributes to cleaner, self documenting code.

Guards vs Validation

While there are many similarities between using guards and using validation (for example, 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.

Chaining Requirements

Guard/Requires uses extension methods that return the original value after each test is performed which allows multiple requirements to be chained together. When Guard/Requires is 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.

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 Regex(@"^[\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 = (List<Address>)addresses
         .RequiresNotNull()
         .RequiresNotEmpty()
         .RequiresDistinct(x => x.AddressType);
   }

   public String FirstName { get; }

   public String LastName { get; }

   public String EmailAddress { get; }

   public IReadOnlyList<Address> Addresses { get; }
}

Note that the cast in Addresses = (List<Address>)addresses... is needed because RequiresDistinct returns an IEnumerable<Address> that can't be assigned to Addresses without the cast back to the original datatype.

Using Guard/Requires

RequirementFailedException

RequirementFailedException is the type of the exception thrown when a requirement specified in a guard method is not met. RequirementFailedException has a Message property that contains text describing the failure. Each Requires... method has an optional customMessage parameter which, if not null, is used for the exception message.

Requiring that a value be initialized

  • RequiresNotNull
  • RequiresNotDefault

Use RequiresNotNull and RequiresNotDefault to express requirements that a value not be null or that a value is not the default for the values's type.

Examples:

   public void RequiresNotNullExample(HashSet<String> items)
   {
      items.RequiresNotNull();
      // ...

   public void RequiresNotNullWithCustomMessageExample(HashSet<String> items)
   {
      items.RequiresNotNull($"{nameof(items)} parameter may not be null");
      // ...

   public void RequiresNotDefaultExample(Int32 offset)
   {
      offset.RequiresNotDefault();
      // ...

   public void RequiresNotDefaultWithCustomMessageExample(String displayText)
   {
      displayText.RequiresNotDefault($"{nameof(displayText)} may not be default");
      // ...

Requiring that a value be within a bounded range

  • RequiresGreaterThan
  • RequiresGreaterThanOrEqualTo
  • RequiresLessThan
  • RequiresLessThanOrEqualTo
  • RequiresValueBetween

Use RequiresGreaterThan, RequiresGreaterThanOrEqualTo, RequiresLessThan, RequiresLessThanOrEqualTo and RequiresValueBetween to express requirements that a value must be within specific bounds. Each method has an overload that allows you to provide an IComparer<T> to use when comparing the value to the required limits.

RequiresValueBetween will throw an ArgumentException if the lowerBound parameter is greater than the upperBound parameter.

All of the IComparer<T> overloads will throw an ArgumentNullException if the comparer parameter is null.

Examples:

   public const Decimal MinimumDelta = 0.01M;
   public const Decimal MaximumDelta = 0.99M;

   public void RequiresGreaterThanExample(Decimal delta)
   {
      delta.RequiresGreaterThan(MinimumDelta);
      // ...

   public void RequiresGreaterThanWithCustomMessageExample(Decimal delta)
   {
      delta.RequiresGreaterThan(MinimumDelta, $"{nameof(delta)} must be greater than {MinimumDelta}");
      // ...

   public void RequiresGreaterThanOrEqualToExample(Decimal delta)
   {
      delta.RequiresGreaterThanOrEqualTo(MinimumDelta);
      // ...

   public void RequiresGreaterThanWithIComparerExample(String value)
   {
      value.RequiresGreaterThan("A", StringComparer.OrdinalIgnoreCase);
      // ...

   public void RequiresGreaterThanOrEqualToWithCustomMessageExample(Decimal delta)
   {
      delta.RequiresGreaterThanOrEqualTo(MinimumDelta, $"{nameof(delta)} must be greater than or equal to {MinimumDelta}");
      // ...

   public void RequiresGreaterThanOrEqualToWithIComparerExample(String value)
   {
      value.RequiresGreaterThanOrEqualTo("A", StringComparer.OrdinalIgnoreCase);
      // ...

   public void RequiresLessThanExample(Decimal delta)
   {
      delta.RequiresLessThan(MaximumDelta);
      // ...

   public void RequiresLessThanWithCustomMessageExample(Decimal delta)
   {
      delta.RequiresLessThan(MaximumDelta, $"{nameof(delta)} must be Less than {MaximumDelta}");
      // ...

   public void RequiresLessThanWithIComparerExample(String value)
   {
      value.RequiresLessThan("Z", StringComparer.OrdinalIgnoreCase);
      // ...

   public void RequiresLessThanOrEqualToExample(Decimal delta)
   {
      delta.RequiresLessThanOrEqualTo(MaximumDelta);
      // ...

   public void RequiresLessThanOrEqualToWithCustomMessageExample(Decimal delta)
   {
      delta.RequiresLessThanOrEqualTo(MaximumDelta, $"{nameof(delta)} must be Less ThanOrEqualTo {MaximumDelta}");
      // ...

   public void RequiresLessThanOrEqualToWithIComparerExample(String value)
   {
      value.RequiresLessThanOrEqualTo("Z", StringComparer.OrdinalIgnoreCase);
      // ...

   public void RequiresValueBetweenExample(Decimal delta)
   {
      delta.RequiresValueBetween(MinimumDelta, MaximumDelta);
      // ...

   public void RequiresValueBetweenExampleWithCustomMessage(Decimal delta)
   {
      delta.RequiresValueBetween(MinimumDelta, MaximumDelta, $"{nameof(delta)} must be between {MinimumDelta} and {MaximumDelta}");
      // ...

   public void RequiresValueBetweenExampleWithIComparerExample(String value)
   {
      value.RequiresValueBetween("A", "z", StringComparer.CurrentCultureIgnoreCase);
      // ...
  • RequiresGreaterThanZero
  • RequiresGreaterThanOrEqualToZero
  • RequiresLessThanZero
  • RequiresLessThanOrEqualToZero

For built-in signed numeric types, you can also use RequiresGreaterThanZero, RequiresGreaterThanOrEqualToZero, RequiresLessThanZero and RequiresLessThanOrEqualToZero to express requirements where zero is the lower or upper bound of the allowed range.

  • BigInteger
  • Decimal
  • Double
  • Half
  • Int16
  • Int32
  • Int64
  • nint
  • SByte
  • Single

In addition, built-in unsigned numeric types support RequiresGreaterThanZero and RequiresLessThanOrEqualToZero. RequiresGreaterThanOrEqualToZero is not supported because it will always pass any unsigned type and RequiresLessThanZero is not supported because it will always fail any unsigned type.

  • Byte
  • nuint
  • SByte
  • UInt16
  • UInt32
  • UInt64

Examples:

   public void RequiresGreaterThanZeroExample(Double length)
   {
      length.RequiresGreaterThanZero();
      // ...

   public void RequiresGreaterThanZeroWithCustomMessage(UInt16 offset)
   {
      offset.RequiresGreaterThanZero($"{nameof(offset)} must be greater than zero");
      // ...

   public void RequiresGreaterThanOrEqualToZeroExample(Int32 offset)
   {
      offset.RequiresGreaterThanOrEqualToZero();
      // ...

   public void RequiresGreaterThanOrEqualToZeroExample(BigInteger total)
   {
      total.RequiresGreaterThanOrEqualToZero($"{nameof(total)} must be greater than or equal to zero");
      // ...

   public void RequiresLessThanZeroExample(Int32 decrement)
   {
      decrement.RequiresGreaterThanZero();
      // ...

   public void RequiresLessThanZeroExampleWithCustomMessage(SByte decrement)
   {
      decrement.RequiresGreaterThanZero($"{nameof(decrement)} must be less than zero");
      // ...

   public void RequiresLessThanOrEqualToZeroExample(Half value)
   {
      value.RequiresLessThanOrEqualToZero();
      // ...

   public void RequiresLessThanOrEqualToZeroExample(Double value)
   {
      value.RequiresLessThanOrEqualToZero($"{nameof(value)} must be less than or equal to zero");
      // ...

Requiring that an enum be defined

  • RequiresEnumIsDefined
  • RequiresEnumIsDefinedAndNotDefault

Use RequiresEnumIsDefined and RequiresEnumIsDefinedAndNotDefault to express requirements that a value be a defined member of an enum type.

RequiresEnumIsDefinedAndNotDefault 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 truly was 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.

Examples:

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

   public void RequiresEnumIsDefinedExample(AddressType addressType)
   {
      addressType.RequiresEnumIsDefined();
      // ...

   public void RequiresEnumIsDefinedWithCustomMessageExample(AddressType addressType)
   {
      addressType.RequiresEnumIsDefined("unrecognized address type");
      // ...

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

   public void RequiresEnumIsDefinedAndNotDefaultExample(Priority priority)
   {
      priority.RequiresEnumIsDefinedAndNotDefault();
      // ...

   public void RequiresEnumIsDefinedAndNotDefaultWithCustomMessageExample(Priority priority)
   {
      priority.RequiresEnumIsDefinedAndNotDefault("priority is not valid");
      // ...

Requiring that strings are valid

  • RequiresMaxLength
  • RequiresMinLength
  • RequiresNotNullOrEmpty
  • RequiresNotNullOrWhiteSpace
  • RequiresNotEmptyOrWhiteSpace
  • RequiresRegexMatch

Use RequiresMaxLength and RequiresMinLength to express requirements for maximum/minimum string lengths.

Use RequiresNotNullOrEmpty and RequiresNotNullOrWhitespace to express requirements that are the inverse of the string static methods IsNullOrEmpty and IsNullOrWhiteSpace.

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.

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.

Examples:

   public const Int32 NameLength = 20;

   public void RequiresMaxLengthExample(String firstName)
   {
      firstName.RequiresMaxLength(NameLength);
      // ...

   public void RequiresMaxLengthWithCustomMessageExample(String firstName)
   {
      firstName.RequiresMaxLength(NameLength, $"name maximum length is {NameLength}");
      // ...

   public const Int32 ReasonMinLength = 5;

   public void RequiresMinLengthExample(String reason)
   {
      reason.RequiresMinLength(ReasonMinLength);
      // ...

   public void RequiresMinLengthWithCustomMessageExample(String reason)
   {
      reason.RequiresMinLength(ReasonMinLength, $"reason must be a minimum of {ReasonMinLength} characters in length");
      // ...

   public void RequiresNotNullOrEmptyExample(String description)
   {
      description.RequiresNotNullOrEmpty();
      // ...

   public void RequiresNotNullOrEmptyWithCustomMessageExample(String description)
   {
      description.RequiresNotNullOrEmpty("description is required");
      // ...

   public void RequiresNotNullOrWhiteSpaceExample(String details)
   {
      details.RequiresNotNullOrWhiteSpace();
      // ...

   public void RequiresNotNullOrWhiteSpaceWithCustomMessageExample(String details)
   {
      details.RequiresNotNullOrWhiteSpace("details are required");
      // ...

   public void RequiresNotEmptyOrWhiteSpaceExample(String description)
   {
      description.RequiresNotEmptyOrWhiteSpace();
      // ...

   public void RequiresNotEmptyOrWhiteSpaceWithCustomMessageExample(String description)
   {
      description.RequiresNotEmptyOrWhiteSpace("non-null description may not be empty or whitespace");
      // ...

   public const String EmailAddressRegex = @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$";

   public void RequiresRegexMatchWithStringRegexExample(String emailAddress)
   {
      emailAddress.RequiresRegexMatch(EmailAddressRegex);
      // ...

   public void RequiresRegexMatchWithStringRegexAndOptionsExample(String emailAddress)
   {
      emailAddress.RequiresRegexMatch(EmailAddressRegex, RegexOptions.IgnoreCase);
      // ...

   public void RequiresRegexMatchWithStringRegexAndCustomMessageExample(String emailAddress)
   {
      emailAddress.RequiresRegexMatch(EmailAddressRegex, customMessage: "invalid email address format");
      // ...

   private static readonly Regex 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]?)$");

   public void RequiresRegexMatchWithRegexObjectExample(String ipAddress)
   {
      ipAddress.RequiresRegexMatch(IpAddressRegex);
      // ...

   public void RequiresRegexMatchWithRegexObjectAndCustomMessageExample(String ipAddress)
   {
      ipAddress.RequiresRegexMatch(IpAddressRegex, "invalid IP address");
      // ...

Requiring an IEnumerable<T> is valid

  • RequiresAll
  • RequiresAny
  • RequiresAtLeast
  • RequiresAtMost
  • RequiresAtMostOne
  • RequiresDistinct
  • RequiresNotEmpty

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

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

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.

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.

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.

Use RequiresDistinct to express a requirement that all of the items in the collection must be unique. RequiresDistinct also has an overload that allows you to provide an IComparer<T> to use when comparing the items in the collection. If the IComparer<T> is null then RequiresDistinct will throw an ArgumentNullException.

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

Exercise caution when using any of the IEnumerable<T> methods as they will partially or fully enumerate the IEnumerable value.

RequiresAll, RequiresAny, RequiresAtLeast, RequiresAtMost and RequiresAtMostOne all 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 these methods will throw an ArgumentNullException.

Examples:

   public void RequiresAllExample(IEnumerable<Int32> evenNumbers)
   {
      evenNumbers.RequiresAll(x => x % 2 == 0);
      // ...

   public void RequiresAllWithCustomMessageExample(IEnumerable<Int32> divisibleBy3)
   {
      divisibleBy3.RequiresAll(x => x % 3 == 0, "All items must be evenly divisible by 3");
      // ...

   public void RequiresAnyExample(IReadOnlyList<Address> addresses)
   {
      addresses.RequiresAny(x => x.AddressType == AddressType.Billing);
      // ...

   public void RequiresAnyWithCustomMessageExample(IReadOnlyList<Address> addresses)
   {
      addresses.RequiresAny(x => x.AddressType == AddressType.Billing, "Billing address is required");
      // ...

   public const Int32 MinimumValidDuration = 100;
   public const Int32 RequiresValidDurations = 5;

   public void RequiresAtLeastExample(IEnumerable<Int32> durations)
   {
      durations.RequiresAtLeast(x => x >= MinimumValidDuration, RequiresValidDurations);
      // ...

   public void RequiresAtLeastWithCustomMessageExample(IEnumerable<Int32> durations)
   {
      durations.RequiresAtLeast(x => x >= MinimumValidDuration, RequiresValidDurations, "Insufficient durations to perform analysis");
      // ...

   public const Int32 MaximuimAllowedShippingAddresses = 3;

   public void RequiresAtMostExample(IReadOnlyList<Address> addresses)
   {
      addresses.RequiresAtMost(x => x.AddressType == AddressType.Shipping, MaximuimAllowedShippingAddresses);
      // ...

   public void RequiresAtMostWithCustomMessageExample(IReadOnlyList<Address> addresses)
   {
      addresses.RequiresAtMost(x => x.AddressType == AddressType.Shipping, MaximuimAllowedShippingAddresses, "Too many shipping addresses on file");
      // ...

   public void RequiresAtMostOneExample(IEnumerable<Address> addresses)
   {
      addresses.RequiresAtMostOne(x => x.AddressType == AddressType.Home);
      // ...

   public void RequiresAtMostOneWithCustomMessageExample(IEnumerable<Address> addresses)
   {
      addresses.RequiresAtMostOne(x => x.AddressType == AddressType.Home, "Only zero or one home addresses allowed");
      // ...

   public void RequiresDistinctExample(IEnumerable<DateOnly> incidentDates)
   {
      incidentDates.RequiresDistinct();
      // ...

   public void RequiresDistinctWithCustomMessageExample(IEnumerable<DateOnly> incidentDates)
   {
      incidentDates.RequiresDistinct("incident dates must be unique");
      // ...

   public void RequiresDistinctWithEqualityComparerExample(List<String> words)
   {
      words.RequiresDistinct(StringComparer.OrdinalIgnoreCase);
      // ...

   public void RequiresDistinctWithEqualityComparerAndCustomMessageExample(List<String> words)
   {
      words.RequiresDistinct(StringComparer.InvariantCulture, "duplicate entry in word list");
      // ...

   public void RequiresDistinctExampleWithSelector(IReadOnlyCollection<Address> addresses)
   {
      addresses.RequiresDistinct(x => x.AddressType);
      // ...

   public void RequiresDistinctExampleWithSelectorAndCustomMessage(IReadOnlyCollection<Address> addresses)
   {
      addresses.RequiresDistinct(x => x.AddressType, "Address types must be unique");
      // ...

   public void RequiresDistinctExampleWithSelectorAndIEqualityComparer(IReadOnlyCollection<Address> addresses)
   {
      addresses.RequiresDistinct(x => x.StateProvince, StringComparer.InvariantCultureIgnoreCase);
      // ...

   public void RequiresDistinctExampleWithSelectorIEqualityComparerAndCustomMessage(IReadOnlyCollection<Address> addresses)
   {
      addresses.RequiresDistinct(x => x.StateProvince, StringComparer.InvariantCultureIgnoreCase, "State/province codes must be unique");
      // ...

   public void RequiresNotEmptyExample(IEnumerable<Guid> identifiers)
   {
      identifiers.RequiresNotEmpty();
      // ...

   public void RequiresNotEmptyWithCustomMessageExample(IEnumerable<Guid> identifiers)
   {
      identifiers.RequiresNotEmpty("identifiers collection may not be empty");
      // ...

Miscellaneous Requirements

  • 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.

Examples:

   public static readonly IReadOnlyCollection<DayOfWeek> WeekDays = new List<DayOfWeek>
      { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };

   public void RequiresMemberOfExample(DayOfWeek day)
   {
      day.RequiresMemberOf(WeekDays);
      // ...

   public void RequiresMemberOfWithCustomMessageExample(DayOfWeek day)
   {
      day.RequiresMemberOf(WeekDays, "day must be Monday through Friday");
      // ...

   public static readonly IReadOnlyCollection<String> SpanishWeekDays = new List<String>
      { "lunes", "martes", "miercoles", "jueves", "viernes" };

   public void RequiresMemberOfWithIEqualityComparerExample(String day)
   {
      day.RequiresMemberOf(SpanishWeekDays, StringComparer.InvariantCultureIgnoreCase);
      // ...

   public void RequiresMemberOfWithIEqualityComparerAndCustomMessageExample(String day)
   {
      day.RequiresMemberOf(SpanishWeekDays, StringComparer.InvariantCultureIgnoreCase, "seleccione lunes, martes, miercoles, jueves o viernes");
      // ...

Requirements that can't be expressed otherwise

  • RequiresConditionIsTrue
  • Requires.ConditionIsTrue

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.

When a requirement can't be tied to a single value (for example, if delivery type is not download then shipping address must be supplied) you can use the static method ConditionIsTrue of the Requires class. This is the one method in Guard/Requires that is not an extension method. Requires.ConditionIsTrue accepts a condition parameter of type Boolean that can test for any condition you choose.

Examples:

   public void RequiresConditionIsTrueExample(Int32 index)
   {
      index.RequiresConditionIsTrue(x => x % 2 == 0);
      // ...

   public void RequiresConditionIsTrueWithCustomMessageExample(Int32 index)
   {
      index.RequiresConditionIsTrue(x => x % 2 == 0, "index must be an even number");
      // ...

enum DeliveryType
{
   Download,
   Package
}

public class Order
{
   // ...
   public DeliveryType DeliveryType { get; }

   public Address? ShippingAddress { get; }
}

   public void RequiresDotConditionIsTrueExample(Order order)
   {
      Requires.ConditionIsTrue(order.DeliveryType == DeliveryType.Download 
                               || (order.DeliveryType == DeliveryType.Package && order.ShippingAddress != null));
      // ...

   public void RequiresDotConditionIsTrueWithCustomMessageExample(Order order)
   {
      Requires.ConditionIsTrue(order.DeliveryType == DeliveryType.Download 
                               || (order.DeliveryType == DeliveryType.Package && order.ShippingAddress != null),
                               "Shipping address is required for package delivery");
      // ...
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  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.
  • net6.0

    • No dependencies.

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 206 11/22/2023
2.0.0 322 11/9/2022
2.0.0-alpha 143 10/23/2022
1.0.1 220 12/28/2021
1.0.0 191 12/25/2021
1.0.0-alpha 159 11/23/2021

Initial pre-release