Skip to content

Pragmatic.Ensure

Guard clauses for parameter validation in .NET — consistent, fast, and intention-revealing.

Every codebase validates inputs, and without a shared approach guard clauses become a patchwork: inconsistent exception types, forgotten guards, and exceptions thrown for business cases that should return typed errors.

_repository = repository ?? throw new ArgumentNullException(nameof(repository)); // dev A
_logger = logger; // dev B forgot
if (id == Guid.Empty) throw new InvalidOperationException("Id cannot be empty"); // dev C, wrong type
if (order is null) throw new KeyNotFoundException($"Order {id} not found"); // dev D, business case

One consistent, readable guard API that throws the right exception with the right message — and a clear boundary: guards are for programmer errors (preconditions), not business outcomes (use Result for those).

using static Pragmatic.Ensure.Ensure;
public OrderService(IOrderRepository repository, ILogger<OrderService> logger)
{
ThrowIfNull(repository);
ThrowIfNull(logger);
// ...
}
public async Task<Order> GetOrderAsync(Guid id)
{
ThrowIfEmpty(id); // ArgumentException with the right message
return await _repository.GetByIdAsync(id);
}

Argument names are captured automatically via [CallerArgumentExpression] — no nameof noise. The guards are aggressively inlined and allocation-free on the success path.

Terminal window
dotnet add package Pragmatic.Ensure

Stable within the 0.8 preview — the guard surface is settled. See the roadmap.

| Concepts | Guards vs Result, the precondition boundary, design | | Getting Started | Your first guards | | API Reference | Every ThrowIf* / Is* / Check*, parameters, null-safety contract | | Best Practices | When to guard vs return a typed error | | Common Mistakes | The most frequent guard pitfalls | | Troubleshooting | Problem/solution guide |

  • .NET 10.0+

Part of the Pragmatic.Design ecosystem — see Licensing. Pragmatic.Ensure is MIT-licensed.