Why does Enumerable.All return true for an empty sequence?

C#.NetLinqEnumerable

C# Problem Overview


var strs = new Collection<string>();
bool b = strs.All(str => str == "ABC");

The code creates an empty collection of string, then tries to determine if all the elements in the collection are "ABC". If you run it, b will be true.

But the collection does not even have any elements in it, let alone any elements that equal to "ABC".

Is this a bug, or is there a reasonable explanation?

C# Solutions


Solution 1 - C#

It's certainly not a bug. It's behaving exactly as documented:

> true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.

Now you can argue about whether or not it should work that way (it seems fine to me; every element of the sequence conforms to the predicate) but the very first thing to check before you ask whether something is a bug, is the documentation. (It's the first thing to check as soon as a method behaves in a way other than what you expected.)

Solution 2 - C#

All requires the predicate to be true for all elements of the sequence. This is explicitly stated in the documentation. It's also the only thing that makes sense if you think of All as being like a logical "and" between the predicate's results for each element. The true you're getting out for the empty sequence is the identity element of the "and" operation. Likewise, the false you get from Any for the empty sequence is the identity for logical "or".

If you think of All as "there are no elements in the sequence that are not", this might make more sense.

Solution 3 - C#

It is true, as nothing (no condition) makes it false.

The docs probably explain it. (Jon Skeet also mentioned something a few years back)

Same goes for Any (the opposite of All) returning false for empty sets.

Edit:

You can imagine All to be implemented semantically the same as:

foreach (var e in elems)
{
  if (!cond(e))
    return false;
}
return true; // no escape from loop

Solution 4 - C#

Most answers here seem to go along the lines of "because that's how is defined". But there is also a logical reason why is defined this way.

When defining a function, you want your function to be as general as possible, such that it can be applied to the largest possible number of cases. Say, for instance, that I want to define the Sum function, which returns the sum of all the numbers in a list. What should it return when the list is empty? If you'd return an arbitrary number x, you'd define the function as the:

  1. Function that returns the sum of all numbers in the given list, or x if the list is empty.

But if x is zero, you can also define it as the

  1. Function that returns x plus the given numbers.

Note that definition 2 implies definition 1, but 1 does not imply 2 when x is not zero, which by itself is enough reason to pick 2 over 1. But also note 2 is more elegant and, in its own right, more general than 1. Is like placing a spotlight farther away so that it lightens a larger area. A lot larger actually. I'm not a mathematician myself but I'm sure they'll find a ton of connections between definition 2 and other mathematical concepts, but not so many related to definition 1 when x is not zero.

In general, you can, and most likely want to return the identity element (the one that leaves the other operand unchanged) whenever you have a function that applies a binary operator over a set of elements and the set is empty. This is the same reason a Product function will return 1 when the list is empty (note that you could just replace "x plus" with "one times" in definition 2). And is the same reason All (which can be thought of as the repeated application of the logical AND operator) will return true when the list is empty (p && true is equivalent to p), and the same reason Any (the OR operator) will return false.

Solution 5 - C#

The method cycles through all elements until it finds one that does not satisfy the condition, or finds none that fail. If none fail, true is returned.

So, if there are no elements, true is returned (since there were none that failed)

Solution 6 - C#

Here is an extension that can do what OP wanted to do:

static bool All<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool mustExist)
{
    foreach (var e in source)
    {
        if (!predicate(e))
            return false;
        mustExist = false;
    }
    return !mustExist;
}

...and as others have pointed out already this is not a bug but well-documented intended behavior.

An alternative solution if one does not wish to write a new extension is:

strs.DefaultIfEmpty().All(str => str == "ABC");

PS: The above does not work if looking for the default value itself! (Which for strings would be null.) In such cases it becomes less elegant with something similar to:

strs.DefaultIfEmpty(string.Empty).All(str => str == null);

If you can enumerate more than once the easiest solution is:

strs.All(predicate) && strs.Any();

i.e simply add a check after that there actually were any element.

Solution 7 - C#

Keeping the implementation aside. Does it really matter if it is true? See if you have some code which iterates over the enumerable and executes some code. if All() is true then that code is still not going to run since the enumerable doesn't have any elements in it.

var hungryDogs = Enumerable.Empty<Dog>();
bool allAreHungry = hungryDogs.All(d=>d.Hungry);    
if (allAreHungry)
    foreach (Dog dog in hungryDogs)
         dog.Feed(biscuits); <--- this line will not run anyway.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCui Pengfei 崔鹏飞View Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#Mr. PuttyView Answer on Stackoverflow
Solution 3 - C#leppieView Answer on Stackoverflow
Solution 4 - C#JuanView Answer on Stackoverflow
Solution 5 - C#Andrew BarberView Answer on Stackoverflow
Solution 6 - C#AnorZakenView Answer on Stackoverflow
Solution 7 - C#Muhammad Hasan KhanView Answer on Stackoverflow