The new API design offers several improvements:
- Better Performance: No expression tree compilation overhead
- Simpler Syntax: Direct method calls instead of lambda expressions
- Return Values: Methods return validated values for chaining
- Modern C# Features: Uses CallerArgumentExpression, nullable annotations, and generic math
- IntelliSense Friendly: Better IDE support and discoverability
Old API:
ThrowIf.Argument.IsNullOrWhiteSpace(() => username);New API:
Guard.Against.NullOrWhiteSpace(username);Old API:
ThrowIf.Argument.IsNull(() => repository);
_repository = repository; // Separate assignmentNew API:
_repository = Guard.Against.Null(repository); // Validate and assignOld API:
ThrowIf.Argument.IsNullOrWhiteSpace(() => input);
var processed = input.Trim().ToUpper();New API:
var processed = Guard.Against.NullOrWhiteSpace(input)
.Trim()
.ToUpper();// Old
ThrowIf.Argument.IsNullOrWhiteSpace(() => name, "Name is required");
// New
var validName = Guard.Against.NullOrWhiteSpace(name, message: "Name is required");// Old
ThrowIf.Argument.IsNullOrEmpty(() => email);
// New
var validEmail = Guard.Against.NullOrEmpty(email);// Old
ThrowIf.Argument.IsNegative(() => age);
// New
var validAge = Guard.Against.Negative(age);// Old
ThrowIf.Argument.IsGreaterThan(() => count, 100);
// New (using range)
var validCount = Guard.Against.OutOfRange(count, 0, 100);// Old
ThrowIf.Argument.IsInThePast(() => appointmentDate);
// New
var validDate = Guard.Against.PastDateTime(appointmentDate);// Old
ThrowIf.Argument.IsNull(() => service);
// New
var validService = Guard.Against.Null(service);// Old
ThrowIf.Argument.IsDefault(() => id);
// New
var validId = Guard.Against.Default(id);Old Pattern:
public class UserService
{
private readonly IUserRepository _repository;
private readonly ILogger _logger;
private readonly string _connectionString;
public UserService(IUserRepository repository, ILogger logger, string connectionString)
{
ThrowIf.Argument.IsNull(() => repository);
ThrowIf.Argument.IsNull(() => logger);
ThrowIf.Argument.IsNullOrEmpty(() => connectionString);
_repository = repository;
_logger = logger;
_connectionString = connectionString;
}
}New Pattern:
public class UserService
{
private readonly IUserRepository _repository;
private readonly ILogger _logger;
private readonly string _connectionString;
public UserService(IUserRepository repository, ILogger logger, string connectionString)
{
_repository = Guard.Against.Null(repository);
_logger = Guard.Against.Null(logger);
_connectionString = Guard.Against.NullOrEmpty(connectionString);
}
}The new API also supports fluent extension methods:
// Using extension methods
var email = userInput
.GuardNullOrWhiteSpace()
.Trim()
.ToLowerInvariant();
// Combining guards
var config = configuration
.GuardNull()
.ConnectionString
.GuardNullOrEmpty();If you need specific exception types (like the old ThrowIf<TException>), you can create wrapper methods:
public static class CustomGuard
{
public static T InvalidOperation<T>(T value, Func<T, bool> condition, string? message = null)
{
if (condition(value))
{
throw new InvalidOperationException(message ?? "Invalid operation");
}
return value;
}
}- Performance: Eliminate expression compilation overhead
- Readability: Cleaner, more intuitive syntax
- Functional Style: Support for method chaining and functional composition
- Type Safety: Better null-safety with nullable reference types
- Modern C#: Leverage latest language features
You can migrate gradually by:
- Keep both APIs during transition
- Update new code to use the new API
- Refactor existing code module by module
- Remove old API once migration is complete
- Method names have changed (e.g.,
IsNull→Null) - Parameters are passed directly, not as expressions
- All methods now return the validated value
- Some validations are combined (e.g., separate greater/less than → range validation)