Using C# LINQ All to Validate Collections
c# linq all: Learn how the C# LINQ All method works, its short-circuiting behavior, empty-sequence semantics, and when to prefer a manual loop.
c# linq all requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The Enumerable.All method in C# is a straightforward way to check whether every element in a sequence satisfies a given condition. It is part of the System.Linq namespace and is available on any IEnumerable<T>. The method takes a predicate and returns true only if the predicate returns true for all elements. If even one element fails, it returns false. This behavior makes it a natural fit for validation logic, invariant checks, and preconditions in business code.
How Enumerable.All Works
The signature of All is public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate). It iterates through the source sequence and applies the predicate to each element. The moment the predicate returns false for an element, the method stops and returns false. If the predicate never returns false, the method returns true. Here is a minimal example:
using System; using System.Collections.Generic; using System.Linq; var numbers = new List<int> { 2, 4, 6, 8 }; bool allEven = numbers.All(n => n % 2 == 0); Console.WriteLine(allEven); // True
In this case, every number is even, so All returns true. If you change the list to include an odd number, All returns false.
Short-Circuiting Behavior
All does not evaluate the entire sequence when it can already determine the result. As soon as the predicate returns false for one element, the iteration stops. This is important for performance, especially when dealing with large collections or expensive predicates. For example:
var largeList = Enumerable.Range(1, 1_000_000); bool allPositive = largeList.All(x => x > 0); // Stops at the first element? Actually first is 1, so it continues until the end.
If the first element fails, the method returns immediately. In the worst case, when all elements satisfy the condition, the method must examine every element. The time complexity is O(n) in the worst case, but short-circuiting can make the average case much faster.
All on an Empty Sequence
A subtle but important detail is that All returns true for an empty sequence. This is a logical consequence of the definition: there is no element that violates the predicate. This is known as vacuous truth. For example:
var empty = new List<int>(); bool result = empty.All(x => x > 0); // True
This behavior can be surprising if you expect All to return false when there are no elements. It is consistent with the mathematical convention that a universal quantification over an empty set is true. If your business logic requires a different behavior, you need to explicitly check for emptiness first.
Performance Considerations
When you need to verify that every element meets a condition, All is often the clearest and most concise option. However, there are performance nuances to keep in mind. For in-memory collections, All is implemented as a simple loop with an early exit. It does not allocate additional memory, aside from the iterator itself. For large collections, the dominant cost is the predicate execution per element.
In some cases, you might be tempted to use Count() and compare it to the length of the collection, but that approach always iterates the entire sequence and is less efficient. For example:
// Less efficient: always scans all elements bool allEven = numbers.Count(n => n % 2 == 0) == numbers.Count; // More efficient: short-circuits on first failure bool allEven = numbers.All(n => n % 2 == 0);
The All method is generally the right choice unless you need the count of matching elements for another purpose.
Null Sources and Null Predicates
All throws an ArgumentNullException if either the source or the predicate is null. This is consistent with the design of most LINQ extension methods. You should always ensure that the collection you call All on is not null, and that the predicate is not null. In practice, a null predicate is rare because you typically pass a lambda expression, but a null source can happen when a method returns null instead of an empty collection. Defensive coding often involves checking for null before calling All, or using the null-conditional operator:
bool? allValid = list?.All(x => x.IsValid); // null if list is null, otherwise bool
If you need a non-nullable result, you can use the null-coalescing operator: bool allValid = list?.All(x => x.IsValid) ?? false;.
All in LINQ to SQL and Entity Framework
When working with IQueryable<T>, such as a DbSet<T> in Entity Framework, All is not executed in memory. Instead, it is translated into a SQL query. The exact translation depends on the provider, but it typically results in a NOT EXISTS or a CASE expression. For example, a query like context.Orders.All(o => o.Total > 0) may be translated to SQL that checks whether any order has a total less than or equal to zero. This translation can be efficient because the database engine can use indexes and statistics. However, you should be aware that the predicate must be an expression that the query provider can translate. If the predicate contains method calls that cannot be translated, an exception will be thrown at runtime.
Common Mistakes and Misconceptions
A frequent mistake is confusing All with Any. Any returns true if at least one element satisfies the predicate, while All requires every element to satisfy it. For example, numbers.Any(n => n > 5) is true if there is at least one number greater than 5, whereas numbers.All(n => n > 5) is true only if all numbers are greater than 5. Another misconception is that All will evaluate the predicate on every element even if an early failure occurs; as discussed, it short-circuits. Additionally, some developers assume that All on a null source returns false, but it throws an exception. Understanding these behaviors prevents subtle bugs.
When to Prefer a Manual Loop
Although All is expressive, there are scenarios where a manual loop is clearer or more efficient. If the predicate has side effects, using All is misleading because the method is intended to be a pure check. A manual loop makes the side effects explicit. Also, if you need to know the index of the first failing element, a loop gives you that information directly:
int firstInvalidIndex = -1; for (int i = 0; i < items.Count; i++) { if (!items[i].IsValid) { firstInvalidIndex = i; break; } }
In most cases, All is the better choice because it communicates intent clearly and reduces boilerplate. The manual loop becomes valuable only when you need additional context about the failure or when the predicate is not side-effect-free.
All with Complex Predicates and Early Exit
When the predicate itself is computationally expensive, short-circuiting becomes even more beneficial. For example, if you are checking a collection of strings for a valid format using a regular expression, All will stop at the first invalid string, saving the cost of validating the rest. However, you should be cautious about using All with predicates that throw exceptions. If an exception occurs inside the predicate, the iteration stops and the exception propagates. This is usually desirable, but it means that the method does not catch exceptions. If you need to collect multiple failures, All is not the right tool; you would use Where and inspect the results.
All and IReadOnlyCollection
All works on any IEnumerable<T>, including arrays, lists, and other collection types. For types that implement IReadOnlyCollection<T> or ICollection<T>, the method still uses the enumerator, not the Count property. This means that even if the collection has a known length, All does not use it to optimize the iteration. The only optimization that might occur is if the underlying enumerator is a struct-based iterator, which can reduce allocation overhead. In practice, the difference is negligible for most applications.
Conclusion
The All method is a fundamental part of LINQ that every C# developer should understand. Its short-circuiting behavior, empty-sequence semantics, and interaction with query providers make it a powerful tool for writing concise and correct validation logic. By knowing when to use All and when to reach for a manual loop, you can write code that is both efficient and maintainable.