So I ran into a situation today where some production code was failing precisely because a method performed exactly as documented in MSDN. Shame on me for not reading the documentation. However, I’m still scratching my head as to why it behaves this way, even if “by design”, since this behavior is exactly opposite what I would have expected (and other, known behaviors) and therefore seems to violate the principle of least surprise.
The All() method allows you to supply a predicate (such as a lambda expression) to test an IQueryable, returning a Boolean value that indicates whether all collection members match the test. So far so good. Here’s where it gets weird. All() also returns true if the collection is empty. This seems completely backwards to me, for the following reasons:
- If the collection is empty, a test like this is, at best, undefined. If my driveway is empty, I cannot assert that all cars parked there are red. With this behavior, on an empty driveway all cars parked there are red AND blue AND checkerboard – all of these expressions would return true.
- For anyone familiar with the SQL notion that NULL != NULL, this is unexpected behavior.
- The
Any()method behaves as expected, and (correctly) returns false because it does not have any members that match the predicate.
So my question is, why does All() behave this way? What problem does it solve? Does this violate the principle of least surprise?
I tagged this question as .NET 3.5, though the behavior also applies to .NET 4.0 as well.
EDIT Ok, so I grasp the logic aspect to this, as so excellently laid out by Jason and the rest of you. Admittedly, an empty collection is something of an edge case. I guess my question is rooted in the struggle that, just because something is logical doesn’t mean it necessarily makes sense if you’re not in the correct frame of mind.
Consider the following statements.
I claim that
S1impliesS2. That is, the statementS1 => S2is true. I will do this by showing that its negation is false. In this case, the negation ofS1 => S2isS1 ^ ~S2; this is becauseS1 => S2is false only whenS1is true andS2is false. What is the negation ofS2? It isWhat is the truth value of
S1 ^ ~S2? Let’s write it outThe only way that
S1 ^ ~S2can be true is if bothS1and~S2are true. ButS1says that my driveway is empty andS2says that there exists a car in my driveway. My driveway can not be both empty and contain a car. Thus, it is impossible forS1and~S2to both be true. Therefore,S1 ^ ~S2is false so its negationS1 => S2is true.Therefore, if your driveway is empty you can assert that all cars parked there are red.
So now let’s consider an
IEnumerable<T> elementsand aPredicate<T> p. Let us suppose thatelementsis empty. We wish to discover the value ofLet’s consider its negation
For
notbto be true, there must be at least onexinelementsfor which!p(x)is true. Butelementsis empty so it is impossible to find anxfor which!p(x)is true. Thereforenotbcan not be true so it must be false. Sincenotbis false, its negation is true. Thereforebis true andelements.All(x => p(x))must be true ifelementsis empty.Here’s one more way to think of this. The predicate
pis true if for allxinelementsyou can not find any for which it is false. But if there are no items inelementsthen it is impossible to find any for which it is false. Thus, for an empty collectionelements,pis true for allxinelementsNow, what about
elements.Any(x => p(x))whenelementsis an emptyIEnumerable<T>andpis aPredicate<T>as above? We already know the result will be false because we know its negation is true, but let’s reason through it anyway; the intuition is valuable. Forelements.Any(x => p(x))to be true there must be at least onexinelementsfor whichp(x)is true. But if there aren’t anyxinelementsit is impossible to find anyxfor whichp(x)is true. Therefore,elements.Any(x => p(x))is false ifelementsis empty.Finally, here’s a related explanation on why
s.StartsWith(String.Empty)is true whensis a non-null instance ofstring: