Collection Validation
Master predicate-based validation for collections with Any and All guards.
Overview
SGuard provides two powerful collection guards:
Any: At least one element matches the predicateAll: All elements match the predicate
Both work with any IEnumerable<T> and accept a predicate (Func<T, bool>). Is.Any, Is.All and the ThrowIf overloads
that take an exception instance also accept a ReadOnlySpan<T> (arrays bind to it on C# 14). A null source or
predicate throws ArgumentNullException.
Any Guard
ThrowIf.Any
Throws if at least one element matches the predicate:
// Throws if any item is null
ThrowIf.Any(items, i => i is null);
// Throws if any number is negative
ThrowIf.Any(numbers, n => n < 0);
// Throws if any order has invalid quantity
ThrowIf.Any(orders, o => o.Quantity <= 0);
Use case: Ensuring no element violates a rule.
Is.Any
Returns true if at least one element matches:
// Check if collection has any positive numbers
bool hasPositive = Is.Any(numbers, n => n > 0);
// Check if any user is active
bool hasActiveUser = Is.Any(users, u => u.IsActive);
// Check if any item is out of stock
bool hasOutOfStock = Is.Any(products, p => p.Stock == 0);
Use case: Finding if a condition exists in the collection.
All Guard
ThrowIf.All
Throws if all elements match the predicate:
// Throws if all numbers are negative
ThrowIf.All(numbers, n => n < 0);
// Throws if all users are inactive
ThrowIf.All(users, u => !u.IsActive);
// Throws if all items are out of stock
ThrowIf.All(products, p => p.Stock == 0);
Use case: Preventing situations where every element has an undesirable property.
Is.All
Returns true if all elements match:
// Check if all numbers are positive
bool allPositive = Is.All(numbers, n => n > 0);
// Check if all users have verified emails
bool allVerified = Is.All(users, u => u.EmailVerified);
// Check if all items are in stock
bool allInStock = Is.All(products, p => p.Stock > 0);
Use case: Verifying that every element satisfies a requirement.
Real-World Examples
Shopping Cart Validation
public class CartValidator
{
public void ValidateCart(Cart cart)
{
ThrowIf.NullOrEmpty(cart);
ThrowIf.NullOrEmpty(cart.Items);
// Ensure no negative quantities
ThrowIf.Any(cart.Items, i => i.Quantity <= 0,
new InvalidOperationException("Cart contains items with invalid quantities"));
// Ensure at least one item is available
if (!Is.Any(cart.Items, i => i.IsAvailable))
{
throw new InvalidOperationException("Cart contains no available items");
}
}
}
User Collection Validation
public void ProcessUsers(List<User> users)
{
ThrowIf.NullOrEmpty(users);
// Reject if any user is null
ThrowIf.Any(users, u => u is null,
new ArgumentException("User list contains null entries"));
// Reject if all users are inactive
ThrowIf.All(users, u => !u.IsActive,
new InvalidOperationException("All users are inactive"));
// Proceed with processing...
}
Order Validation
public class OrderService
{
private readonly ILogger<OrderService> logger;
public void ValidateOrders(IEnumerable<Order> orders)
{
ThrowIf.NullOrEmpty(orders);
// Ensure no orders have invalid totals
ThrowIf.Any(orders, o => o.Total <= 0,
new InvalidOperationException("One or more orders have invalid totals"));
// Warn if all orders are on hold
if (Is.All(orders, o => o.Status == OrderStatus.OnHold))
{
logger.LogWarning("All orders are on hold");
}
}
}
Stock Level Validation
public class InventoryManager
{
private readonly INotificationService notificationService;
public void CheckInventory(List<Product> products)
{
// ThrowIf.All throws for an empty list, so reject that case first with its own message
ThrowIf.NullOrEmpty(products);
// Alert if any product is out of stock
if (Is.Any(products, p => p.Stock == 0))
{
notificationService.Send("Some products are out of stock");
}
// Critical alert if all products are out of stock
ThrowIf.All(products, p => p.Stock == 0,
new InvalidOperationException("All products are out of stock"));
}
}
Permission Validation
public void ExecuteAction(User user, string action)
{
ThrowIf.NullOrEmpty(user);
ThrowIf.NullOrEmpty(user, u => u.Roles);
// Ensure user has at least one role with permission
if (!Is.Any(user.Roles, r => r.HasPermission(action)))
{
throw new UnauthorizedAccessException($"User lacks permission for '{action}'");
}
}
Combining Any and All
You can combine both guards for comprehensive validation:
public void ValidateTestResults(List<TestResult> results)
{
ThrowIf.NullOrEmpty(results);
// Ensure at least one test passed
if (!Is.Any(results, r => r.Passed))
{
throw new Exception("All tests failed");
}
// Warn if any critical test failed
if (Is.Any(results, r => !r.Passed && r.IsCritical))
{
logger.LogError("Critical test(s) failed");
}
// Success if all tests passed
if (Is.All(results, r => r.Passed))
{
logger.LogInformation("All tests passed");
}
}
With Custom Exceptions
public class DomainValidationException : Exception
{
public DomainValidationException(string message) : base(message) { }
}
ThrowIf.Any(items, i => i.IsInvalid,
new DomainValidationException("Collection contains invalid items"));
ThrowIf.All(users, u => u.IsBlocked,
new DomainValidationException("All users are blocked"));
With Callbacks
// Log when any item fails validation
ThrowIf.Any(
items,
i => i.IsInvalid,
SGuardCallbacks.OnFailure(() => logger.LogError("Invalid items detected")));
// Metrics for all-valid collections
bool allValid = Is.All(
items,
i => i.IsValid,
SGuardCallbacks.OnSuccess(() => metrics.Increment("validation.all.valid")));
Empty Collections
Empty sources follow LINQ semantics, for the IEnumerable<T> and ReadOnlySpan<T> overloads alike:
// Returns false (no elements match)
Is.Any(emptyList, x => true); // false
// Returns true (vacuous truth: all zero elements match)
Is.All(emptyList, x => false); // true
As a consequence:
ThrowIf.Anynever throws for an empty source.ThrowIf.Allalways throws for an empty source (AllException, or your exception), whatever the predicate. A check likeThrowIf.All(users, u => !u.IsActive)reports "all users are inactive" for an empty list.Is.All(items, i => i.IsValid)returnstruefor an empty list, so it doesn't prove that there is anything valid.
Best practice: Check for null/empty collections first:
ThrowIf.NullOrEmpty(items);
ThrowIf.Any(items, i => i is null); // Safe now
Performance Considerations
- Short-circuit evaluation: Both guards stop as soon as the result is determined
Anystops at the first matching elementAllstops at the first non-matching element
- LINQ-compatible: Works with any
IEnumerable<T>, including LINQ queries - Predicate caching: Predicates are not cached (unlike selectors in
NullOrEmpty)
Common Patterns
At Least One Valid Item
if (!Is.Any(items, i => i.IsValid))
{
throw new Exception("No valid items found");
}
No Invalid Items
ThrowIf.Any(items, i => !i.IsValid);
All Items Valid
if (!Is.All(items, i => i.IsValid))
{
throw new Exception("Some items are invalid");
}
Next Steps
- String Comparisons - Culture-aware string validation
- Real-World Examples - Complete validation scenarios
- API Reference - Complete Is API