Documentation Index
Fetch the complete documentation index at: https://ancplua.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Guard clauses for argument validation with CallerArgumentExpression for automatic parameter name capture.
The Problem
Standard null checks are verbose and require nameof:
// BAD: Verbose and repetitive
public void Process(string? name, IService? service)
{
_name = name ?? throw new ArgumentNullException(nameof(name));
_service = service ?? throw new ArgumentNullException(nameof(service));
}
The Solution
// GOOD: Clean and expressive
public void Process(string? name, IService? service)
{
_name = Guard.NotNull(name);
_service = Guard.NotNull(service);
}
The parameter name is automatically captured - no nameof required.
Null Validation
NotNull
Validates non-null and returns the value:
// Throws ArgumentNullException if null
var validated = Guard.NotNull(possiblyNullValue);
// Can be used inline
ProcessItem(Guard.NotNull(item));
// In constructors
public MyClass(ILogger? logger, IConfig? config)
{
_logger = Guard.NotNull(logger);
_config = Guard.NotNull(config);
}
NotNullOrElse
Returns value or default (no exception):
// Reference types
var name = Guard.NotNullOrElse(user?.Name, "Anonymous");
var service = Guard.NotNullOrElse(optionalService, DefaultService.Instance);
// Value types
int count = Guard.NotNullOrElse(maybeCount, 0);
// Lazy evaluation for expensive defaults
var config = Guard.NotNullOrElse(optionalConfig, () => LoadDefaultConfig());
Member Validation
Validate an object and its member in one call:
// Validates both config and config.ConnectionString are non-null
public void Configure(Config? config)
{
var connectionString = Guard.NotNullWithMember(config, config?.ConnectionString);
// Throws ArgumentNullException if config is null
// Throws ArgumentException if config.ConnectionString is null
}
// When the parent is known non-null, validate only the member
public void Process(Config config)
{
var timeout = Guard.MemberNotNull(config, config.Timeout);
// Throws ArgumentException if config.Timeout is null
}
String Validation
NotNullOrEmpty
Validates non-null and non-empty strings:
public void SetName(string? name)
{
_name = Guard.NotNullOrEmpty(name);
// Throws ArgumentNullException if null
// Throws ArgumentException if empty
}
NotNullOrWhiteSpace
Validates strings contain meaningful content:
public void SetDescription(string? description)
{
_description = Guard.NotNullOrWhiteSpace(description);
// Throws if null, empty, or only whitespace
}
With Defaults
// Return default if null/empty
var displayName = Guard.NotNullOrEmptyOrElse(user.DisplayName, user.Username);
// Return default if null/empty/whitespace
var title = Guard.NotNullOrWhiteSpaceOrElse(article.Title, "Untitled");
// Lazy evaluation for expensive defaults
var config = Guard.NotNullOrEmptyOrElse(optionalConfig, () => LoadDefaultConfig());
String Length Validation
HasLength
Validates exact string length:
public void SetCountryCode(string? code)
{
_code = Guard.HasLength(code, 2); // ISO country codes
// Throws ArgumentNullException if null
// Throws ArgumentException if length != 2
}
HasMinLength / HasMaxLength
Validates minimum or maximum string length:
public void SetPassword(string? password)
{
_password = Guard.HasMinLength(password, 8);
// Throws if length < 8
}
public void SetUsername(string? username)
{
_username = Guard.HasMaxLength(username, 50);
// Throws if length > 50
}
HasLengthBetween
Validates string length is within a range (inclusive):
public void SetDisplayName(string? name)
{
_name = Guard.HasLengthBetween(name, 3, 100);
// Throws if length < 3 or length > 100
}
Collection Validation
NotNullOrEmpty
public void ProcessItems(IReadOnlyCollection<Item>? items)
{
var validItems = Guard.NotNullOrEmpty(items);
// Throws ArgumentNullException if null
// Throws ArgumentException if empty
foreach (var item in validItems)
{
// Process items
}
}
NoDuplicates
Validates that a collection contains no duplicate elements:
public void SetIds(IEnumerable<int> ids)
{
Guard.NoDuplicates(ids);
// Throws ArgumentException with "Duplicate value found: {value}"
}
// IReadOnlyList overload returns the validated list
public void Process(IReadOnlyList<string>? names)
{
var validNames = Guard.NoDuplicates(names);
// Validates non-null AND no duplicates
}
// With custom equality comparer
public void SetNames(IEnumerable<string> names)
{
Guard.NoDuplicates(names, StringComparer.OrdinalIgnoreCase);
// Case-insensitive duplicate detection
}
Value Type Validation
NotDefault
Validates that a value type is not its default value:
public void SetId(Guid id)
{
_id = Guard.NotDefault(id);
// Throws ArgumentException if id == default(Guid)
}
public void SetTimestamp(DateTime timestamp)
{
_timestamp = Guard.NotDefault(timestamp);
// Throws if timestamp == default(DateTime)
}
NotEmpty (Guid)
Validates that a Guid is not empty:
public void SetUserId(Guid userId)
{
_userId = Guard.NotEmpty(userId);
// Throws ArgumentException if userId == Guid.Empty
}
NotEmpty for Guid is semantically clearer than NotDefault when working
with identifiers, though they produce the same result for Guid.
Set Membership Validation
OneOf
Validates that a value is one of the allowed values:
public void SetProtocol(string protocol)
{
_protocol = Guard.OneOf(protocol, new[] { "http", "https", "ftp" });
// Throws ArgumentException if not in allowed set
}
// O(1) lookup with HashSet for large allowed sets
private static readonly HashSet<string> AllowedContentTypes = new(StringComparer.OrdinalIgnoreCase)
{
"application/json",
"application/xml",
"text/plain"
};
public void SetContentType(string contentType)
{
_contentType = Guard.OneOf(contentType, AllowedContentTypes);
}
NotOneOf
Validates that a value is not one of the disallowed values:
public void SetPort(int port)
{
_port = Guard.NotOneOf(port, new[] { 0, 80, 443 }); // Reserved ports
// Throws ArgumentException if in disallowed set
}
// O(1) lookup with HashSet
private static readonly HashSet<int> ReservedPorts = new() { 0, 80, 443, 8080 };
public void SetCustomPort(int port)
{
_port = Guard.NotOneOf(port, ReservedPorts);
}
Range Validation
InRange
Validates value is within bounds (inclusive):
public void SetPage(int page)
{
_page = Guard.InRange(page, 1, 100);
// Throws ArgumentOutOfRangeException if outside range
}
// Works with any IComparable<T>
var temperature = Guard.InRange(temp, -40.0, 100.0);
var date = Guard.InRange(eventDate, DateTime.Today, DateTime.Today.AddYears(1));
ValidIndex
Validates array/list index:
public T GetItem(int index)
{
Guard.ValidIndex(index, _items.Count);
return _items[index];
}
Numeric Validation
All numeric validation methods use [MethodImpl(AggressiveInlining)] for hot paths and are available for int, long, double, and decimal types.
Basic Constraints
// Value cannot be zero
var divisor = Guard.NotZero(value);
// Value must be >= 0
var count = Guard.NotNegative(value);
// Value must be > 0
var quantity = Guard.Positive(value);
Boundary Constraints
// Upper bound (inclusive): value <= max
var percentage = Guard.NotGreaterThan(value, 100);
// Lower bound (inclusive): value >= min
var temperature = Guard.NotLessThan(value, -273.15);
// Upper bound (exclusive): value < max
var index = Guard.LessThan(value, array.Length);
// Lower bound (exclusive): value > min
var priority = Guard.GreaterThan(value, 0);
Double-Specific Validation
// Validates value is not NaN
var result = Guard.NotNaN(calculatedValue);
// Validates value is finite (not NaN or Infinity)
var coefficient = Guard.Finite(inputValue);
Double comparison methods (NotNegative, Positive, etc.) correctly handle
NaN values by throwing ArgumentOutOfRangeException - NaN is not a valid
number.
File System Validation
FileExists / DirectoryExists
Validates that a file or directory exists at the specified path:
public void LoadConfig(string? path)
{
var validPath = Guard.FileExists(path);
// Throws ArgumentNullException if null
// Throws ArgumentException if empty or file doesn't exist
var content = File.ReadAllText(validPath);
}
public void ProcessDirectory(string? path)
{
var validPath = Guard.DirectoryExists(path);
foreach (var file in Directory.GetFiles(validPath)) { }
}
Path Character Validation
// Validates filename contains no invalid characters
var fileName = Guard.ValidFileName(name);
// Throws if name contains characters like: \ / : * ? " < > |
// Nullable variant - returns null if input is null
var optionalName = Guard.ValidFileNameOrNull(name);
// Validates path contains no invalid characters
var validPath = Guard.ValidPath(userInput);
// Nullable variant
var optionalPath = Guard.ValidPathOrNull(userInput);
Extension Validation
// ValidExtension requires NO leading dot
var ext = Guard.ValidExtension("txt"); // Returns "txt"
Guard.ValidExtension(".txt"); // Throws - no leading dot allowed
Guard.ValidExtension("path/txt"); // Throws - no separators allowed
// NormalizedExtension accepts both and ensures leading dot
var ext1 = Guard.NormalizedExtension("txt"); // Returns ".txt"
var ext2 = Guard.NormalizedExtension(".txt"); // Returns ".txt"
Type Validation
DefinedEnum
Validates that an enum value is defined:
public void SetStatus(Status status)
{
_status = Guard.DefinedEnum(status);
// Throws ArgumentOutOfRangeException for undefined values like (Status)999
}
Type Constraints
// Validates type is not Nullable<T>
public void Register(Type type)
{
Guard.NotNullableType(type);
// Throws if type is int?, bool?, etc.
}
// Validates type implements/inherits from T
public void RegisterService(Type implementationType)
{
Guard.AssignableTo<IService>(implementationType);
// Throws if implementationType doesn't implement IService
}
// Validates T can be assigned to type
public void RegisterHandler(Type handlerType)
{
Guard.AssignableFrom<BaseHandler>(handlerType);
// Throws if BaseHandler isn't assignable to handlerType
}
Condition Validation
That
Validates a boolean condition:
public void SetAge(int age)
{
Guard.That(age >= 0, "Age cannot be negative.");
_age = age;
}
Satisfies
Validates value matches a predicate:
public void SetEmail(string email)
{
_email = Guard.Satisfies(email, e => e.Contains('@'), "Invalid email format.");
}
// Complex validation
_config = Guard.Satisfies(config, c =>
c.Timeout > TimeSpan.Zero && c.MaxRetries >= 0,
"Invalid configuration values.");
Unreachable Code
Mark code paths that should never execute:
// In statement context
switch (status)
{
case Status.Active: return "Active";
case Status.Inactive: return "Inactive";
default: Guard.Unreachable();
}
// In expression context (switch expressions)
return status switch
{
Status.Active => "Active",
Status.Inactive => "Inactive",
_ => Guard.Unreachable<string>()
};
// With custom message
Guard.Unreachable("All enum values should be handled");
UnreachableIf
Throws only when a condition is true, useful for asserting invariants:
// Assert an invariant that should never be violated
Guard.UnreachableIf(list.Count < 0, "Count should never be negative");
// In validation logic
Guard.UnreachableIf(
state == ProcessState.Running && !hasStarted,
"Cannot be running without having started");
Unreachable and UnreachableIf automatically capture caller info
(CallerMemberName, CallerFilePath, CallerLineNumber) to provide detailed
exception messages for debugging.
Examples
Constructor Validation
public class OrderProcessor
{
private readonly IOrderRepository _repository;
private readonly IPaymentService _payment;
private readonly ILogger _logger;
public OrderProcessor(
IOrderRepository? repository,
IPaymentService? payment,
ILogger? logger)
{
_repository = Guard.NotNull(repository);
_payment = Guard.NotNull(payment);
_logger = Guard.NotNull(logger);
}
}
Method Parameter Validation
public async Task<Order> CreateOrderAsync(
string? customerId,
IReadOnlyCollection<OrderItem>? items,
decimal discount)
{
var validCustomerId = Guard.NotNullOrWhiteSpace(customerId);
var validItems = Guard.NotNullOrEmpty(items);
var validDiscount = Guard.InRange(discount, 0m, 100m);
// All parameters validated
return await ProcessOrder(validCustomerId, validItems, validDiscount);
}
Configuration Validation
public class ServerConfig
{
public int Port { get; }
public string Host { get; }
public TimeSpan Timeout { get; }
public ServerConfig(int port, string? host, TimeSpan timeout)
{
Port = Guard.InRange(port, 1, 65535);
Host = Guard.NotNullOrWhiteSpace(host);
Timeout = Guard.Satisfies(timeout, t => t > TimeSpan.Zero, "Timeout must be positive.");
}
}
File Processing
public void ProcessFile(string? inputPath, string? outputFileName)
{
// Validate input file exists
var input = Guard.FileExists(inputPath);
// Validate output filename is valid
var output = Guard.ValidFileName(outputFileName);
// Normalize the extension
var ext = Guard.NormalizedExtension("txt"); // Returns ".txt"
// Process...
}
// Optional filename validation (returns null if input is null)
public void SaveFile(string? fileName)
{
var validName = Guard.ValidFileNameOrNull(fileName);
if (validName is not null)
{
// Save with validated name
}
}
API Reference
Null Validation
| Method | Throws | Description |
|---|
NotNull<T>(value) | ArgumentNullException | Validates non-null |
NotNullOrElse<T>(value, default) | - | Returns value or default |
NotNullOrElse<T>(value, Func<T>) | - | Lazy default evaluation |
NotNullWithMember<TParam, TMember>(obj, member) | ArgumentNullException, ArgumentException | Validates both object and member |
MemberNotNull<TParam, TMember>(obj, member) | ArgumentException | Validates member of non-null object |
String Validation
| Method | Throws | Description |
|---|
NotNullOrEmpty(string) | ArgumentNullException, ArgumentException | Validates non-null, non-empty |
NotNullOrWhiteSpace(string) | ArgumentNullException, ArgumentException | Validates has content |
NotNullOrEmptyOrElse(string, default) | - | Returns value or default |
NotNullOrEmptyOrElse(string, Func<string>) | - | Lazy default evaluation |
NotNullOrWhiteSpaceOrElse(string, default) | - | Returns value or default |
NotNullOrWhiteSpaceOrElse(string, Func<string>) | - | Lazy default evaluation |
HasLength(string, int) | ArgumentNullException, ArgumentException | Exact length |
HasMinLength(string, int) | ArgumentNullException, ArgumentException | Minimum length |
HasMaxLength(string, int) | ArgumentNullException, ArgumentException | Maximum length |
HasLengthBetween(string, int, int) | ArgumentNullException, ArgumentException | Length in range |
Collection Validation
| Method | Throws | Description |
|---|
NotNullOrEmpty<T>(collection) | ArgumentNullException, ArgumentException | Validates collection |
NoDuplicates<T>(IEnumerable<T>) | ArgumentException | Validates no duplicates |
NoDuplicates<T>(IEnumerable<T>, comparer) | ArgumentException | With custom equality |
NoDuplicates<T>(IReadOnlyList<T>) | ArgumentNullException, ArgumentException | Validates and returns list |
NoDuplicates<T>(IReadOnlyList<T>, comparer) | ArgumentNullException, ArgumentException | With custom equality |
Value Type Validation
| Method | Throws | Description |
|---|
NotDefault<T>(T) | ArgumentException | Value type not default |
NotEmpty(Guid) | ArgumentException | Guid is not Guid.Empty |
Set Membership Validation
| Method | Throws | Description |
|---|
OneOf<T>(T, T[]) | ArgumentException | Value in allowed set |
OneOf<T>(T, HashSet<T>) | ArgumentException | O(1) lookup variant |
NotOneOf<T>(T, T[]) | ArgumentException | Value not in disallowed set |
NotOneOf<T>(T, HashSet<T>) | ArgumentException | O(1) lookup variant |
File System Validation
| Method | Throws | Description |
|---|
FileExists(path) | ArgumentNullException, ArgumentException | Validates file exists |
DirectoryExists(path) | ArgumentNullException, ArgumentException | Validates directory exists |
ValidFileName(name) | ArgumentNullException, ArgumentException | No invalid filename chars |
ValidFileNameOrNull(name) | ArgumentException | Nullable variant |
ValidPath(path) | ArgumentNullException, ArgumentException | No invalid path chars |
ValidPathOrNull(path) | ArgumentException | Nullable variant |
ValidExtension(ext) | ArgumentNullException, ArgumentException | No leading dot, no separators |
NormalizedExtension(ext) | ArgumentNullException, ArgumentException | Ensures leading dot |
Type Validation
| Method | Throws | Description |
|---|
DefinedEnum<T>(value) | ArgumentOutOfRangeException | Validates enum is defined |
NotNullableType(type) | ArgumentNullException, ArgumentException | Not Nullable<T> |
AssignableTo<T>(type) | ArgumentNullException, ArgumentException | Type implements T |
AssignableFrom<T>(type) | ArgumentNullException, ArgumentException | T assignable to type |
Numeric Validation (int, long, double, decimal)
| Method | Throws | Description |
|---|
NotZero(value) | ArgumentOutOfRangeException | Value != 0 |
NotNegative(value) | ArgumentOutOfRangeException | Value >= 0 |
Positive(value) | ArgumentOutOfRangeException | Value > 0 |
NotGreaterThan(value, max) | ArgumentOutOfRangeException | value ≤ max |
NotLessThan(value, min) | ArgumentOutOfRangeException | value ≥ min |
LessThan(value, max) | ArgumentOutOfRangeException | value < max |
GreaterThan(value, min) | ArgumentOutOfRangeException | value > min |
InRange<T>(value, min, max) | ArgumentOutOfRangeException | min ≤ value ≤ max |
ValidIndex(index, count) | ArgumentOutOfRangeException | 0 ≤ index < count |
Double-Specific
| Method | Throws | Description |
|---|
NotNaN(value) | ArgumentOutOfRangeException | Value is not NaN |
Finite(value) | ArgumentOutOfRangeException | Not NaN or Infinity |
Condition Validation
| Method | Throws | Description |
|---|
That(condition, message) | ArgumentException | Validates condition |
Satisfies<T>(value, predicate, message) | ArgumentException | Validates with predicate |
Unreachable(message?) | InvalidOperationException | Marks unreachable code |
Unreachable<T>(message?) | InvalidOperationException | For expression contexts |
UnreachableIf(condition, message?) | InvalidOperationException | Throws only when condition true |
All validation methods use CallerArgumentExpressionAttribute to
automatically capture the parameter name in exception messages - no nameof()
required.