> ## Documentation Index
> Fetch the complete documentation index at: https://ancplua.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Guard

> Clean argument validation with automatic parameter name capture

Guard clauses for argument validation with `CallerArgumentExpression` for automatic parameter name capture.

## The Problem

Standard null checks are verbose and require `nameof`:

```csharp theme={null}
// 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

```csharp theme={null}
// 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:

```csharp theme={null}
// 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):

```csharp theme={null}
// 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:

```csharp theme={null}
// 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:

```csharp theme={null}
public void SetName(string? name)
{
    _name = Guard.NotNullOrEmpty(name);
    // Throws ArgumentNullException if null
    // Throws ArgumentException if empty
}
```

### NotNullOrWhiteSpace

Validates strings contain meaningful content:

```csharp theme={null}
public void SetDescription(string? description)
{
    _description = Guard.NotNullOrWhiteSpace(description);
    // Throws if null, empty, or only whitespace
}
```

### With Defaults

```csharp theme={null}
// 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:

```csharp theme={null}
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:

```csharp theme={null}
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):

```csharp theme={null}
public void SetDisplayName(string? name)
{
    _name = Guard.HasLengthBetween(name, 3, 100);
    // Throws if length < 3 or length > 100
}
```

## Collection Validation

### NotNullOrEmpty

```csharp theme={null}
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:

```csharp theme={null}
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:

```csharp theme={null}
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:

```csharp theme={null}
public void SetUserId(Guid userId)
{
    _userId = Guard.NotEmpty(userId);
    // Throws ArgumentException if userId == Guid.Empty
}
```

<Note>
  `NotEmpty` for Guid is semantically clearer than `NotDefault` when working
  with identifiers, though they produce the same result for Guid.
</Note>

## Set Membership Validation

### OneOf

Validates that a value is one of the allowed values:

```csharp theme={null}
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:

```csharp theme={null}
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):

```csharp theme={null}
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:

```csharp theme={null}
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

```csharp theme={null}
// 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

```csharp theme={null}
// 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

```csharp theme={null}
// Validates value is not NaN
var result = Guard.NotNaN(calculatedValue);

// Validates value is finite (not NaN or Infinity)
var coefficient = Guard.Finite(inputValue);
```

<Note>
  Double comparison methods (`NotNegative`, `Positive`, etc.) correctly handle
  NaN values by throwing `ArgumentOutOfRangeException` - NaN is not a valid
  number.
</Note>

## File System Validation

### FileExists / DirectoryExists

Validates that a file or directory exists at the specified path:

```csharp theme={null}
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

```csharp theme={null}
// 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

```csharp theme={null}
// 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:

```csharp theme={null}
public void SetStatus(Status status)
{
    _status = Guard.DefinedEnum(status);
    // Throws ArgumentOutOfRangeException for undefined values like (Status)999
}
```

### Type Constraints

```csharp theme={null}
// 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:

```csharp theme={null}
public void SetAge(int age)
{
    Guard.That(age >= 0, "Age cannot be negative.");
    _age = age;
}
```

### Satisfies

Validates value matches a predicate:

```csharp theme={null}
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:

```csharp theme={null}
// 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:

```csharp theme={null}
// 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");
```

<Tip>
  `Unreachable` and `UnreachableIf` automatically capture caller info
  (`CallerMemberName`, `CallerFilePath`, `CallerLineNumber`) to provide detailed
  exception messages for debugging.
</Tip>

## Examples

### Constructor Validation

```csharp theme={null}
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

```csharp theme={null}
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

```csharp theme={null}
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

```csharp theme={null}
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&lt;T&gt;(value)`                               | `ArgumentNullException`                      | Validates non-null                  |
| `NotNullOrElse&lt;T&gt;(value, default)`                | -                                            | Returns value or default            |
| `NotNullOrElse&lt;T&gt;(value, Func&lt;T&gt;)`          | -                                            | Lazy default evaluation             |
| `NotNullWithMember&lt;TParam, TMember&gt;(obj, member)` | `ArgumentNullException`, `ArgumentException` | Validates both object and member    |
| `MemberNotNull&lt;TParam, TMember&gt;(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&lt;T&gt;(collection)`                     | `ArgumentNullException`, `ArgumentException` | Validates collection       |
| `NoDuplicates&lt;T&gt;(IEnumerable&lt;T&gt;)`             | `ArgumentException`                          | Validates no duplicates    |
| `NoDuplicates&lt;T&gt;(IEnumerable&lt;T&gt;, comparer)`   | `ArgumentException`                          | With custom equality       |
| `NoDuplicates&lt;T&gt;(IReadOnlyList&lt;T&gt;)`           | `ArgumentNullException`, `ArgumentException` | Validates and returns list |
| `NoDuplicates&lt;T&gt;(IReadOnlyList&lt;T&gt;, comparer)` | `ArgumentNullException`, `ArgumentException` | With custom equality       |

### Value Type Validation

| Method                   | Throws              | Description            |
| ------------------------ | ------------------- | ---------------------- |
| `NotDefault&lt;T&gt;(T)` | `ArgumentException` | Value type not default |
| `NotEmpty(Guid)`         | `ArgumentException` | Guid is not Guid.Empty |

### Set Membership Validation

| Method                                   | Throws              | Description                 |
| ---------------------------------------- | ------------------- | --------------------------- |
| `OneOf&lt;T&gt;(T, T[])`                 | `ArgumentException` | Value in allowed set        |
| `OneOf&lt;T&gt;(T, HashSet&lt;T&gt;)`    | `ArgumentException` | O(1) lookup variant         |
| `NotOneOf&lt;T&gt;(T, T[])`              | `ArgumentException` | Value not in disallowed set |
| `NotOneOf&lt;T&gt;(T, HashSet&lt;T&gt;)` | `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&lt;T&gt;(value)`   | `ArgumentOutOfRangeException`                | Validates enum is defined |
| `NotNullableType(type)`         | `ArgumentNullException`, `ArgumentException` | Not `Nullable<T>`         |
| `AssignableTo&lt;T&gt;(type)`   | `ArgumentNullException`, `ArgumentException` | Type implements T         |
| `AssignableFrom&lt;T&gt;(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&lt;T&gt;(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&lt;T&gt;(value, predicate, message)` | `ArgumentException`         | Validates with predicate        |
| `Unreachable(message?)`                         | `InvalidOperationException` | Marks unreachable code          |
| `Unreachable&lt;T&gt;(message?)`                | `InvalidOperationException` | For expression contexts         |
| `UnreachableIf(condition, message?)`            | `InvalidOperationException` | Throws only when condition true |

<Note>
  All validation methods use `CallerArgumentExpressionAttribute` to
  automatically capture the parameter name in exception messages - no `nameof()`
  required.
</Note>
