Why does IQueryable.All() return true on an empty collection?

后端 未结 11 1644
隐瞒了意图╮
隐瞒了意图╮ 2020-12-01 04:46

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 d

相关标签:
11条回答
  • 2020-12-01 05:40

    I think it makes sense. In logic, the complement of FOR ALL is NOT (THERE EXIST). FOR ALL is like All(). THERE EXIST is like Any().

    So IQueryable.All() is equivalent to !IQueryable.Any(). If your IQueryable is empty, then both returns true based on MSDN doc.

    0 讨论(0)
  • 2020-12-01 05:43

    If my driveway is empty, I cannot assert that all cars parked there are red.

    Consider the following statements.

    S1: My driveway is empty.

    S2: All the cars parked in my driveway are red.

    I claim that S1 implies S2. That is, the statement S1 => S2 is true. I will do this by showing that its negation is false. In this case, the negation of S1 => S2 is S1 ^ ~S2; this is because S1 => S2 is false only when S1 is true and S2 is false. What is the negation of S2? It is

    ~S2: There exists a car parked in my driveway that is not red.

    What is the truth value of S1 ^ ~S2? Let's write it out

    S1 ^ ~S2: My driveway is empty and there exists a car parked in my driveway that is not red.

    The only way that S1 ^ ~S2 can be true is if both S1 and ~S2 are true. But S1 says that my driveway is empty and S2 says that there exists a car in my driveway. My driveway can not be both empty and contain a car. Thus, it is impossible for S1 and ~S2 to both be true. Therefore, S1 ^ ~S2 is false so its negation S1 => S2 is 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> elements and a Predicate<T> p. Let us suppose that elements is empty. We wish to discover the value of

    bool b = elements.All(x => p(x));
    

    Let's consider its negation

    bool notb = elements.Any(x => !p(x));
    

    For notb to be true, there must be at least one x in elements for which !p(x) is true. But elements is empty so it is impossible to find an x for which !p(x) is true. Therefore notb can not be true so it must be false. Since notb is false, its negation is true. Therefore b is true and elements.All(x => p(x)) must be true if elements is empty.

    Here's one more way to think of this. The predicate p is true if for all x in elements you can not find any for which it is false. But if there are no items in elements then it is impossible to find any for which it is false. Thus, for an empty collection elements, p is true for all x in elements

    Now, what about elements.Any(x => p(x)) when elements is an empty IEnumerable<T> and p is a Predicate<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. For elements.Any(x => p(x)) to be true there must be at least one x in elements for which p(x) is true. But if there aren't any x in elements it is impossible to find any x for which p(x) is true. Therefore, elements.Any(x => p(x)) is false if elements is empty.

    Finally, here's a related explanation on why s.StartsWith(String.Empty) is true when s is a non-null instance of string:

    0 讨论(0)
  • 2020-12-01 05:45

    Any() and All() are just implementations of the usual mathematical operators ∃ (the "existential quatifier" or "there exists") and ∀ (the "universal quatifier" or "for all").

    "Any" means that there exists some item for which the predicate is true. For the empty collection, this would be false.

    "All" means that there does not exist any item for which the predicate is false. For the empty collection, this would always be true.

    0 讨论(0)
  • 2020-12-01 05:45

    All(x => x.Predicate) is the opposite of Any(x => !x.Predicate) ("Are all cars red?" is the opposite of "Are there any cars that aren't red?").

    Any(x => !x.Predicate) returns false for empty collections (which appears natural for the common understanding of "any").

    Hence All(x => x.Predicate) should (and does) return true for empty collections.

    0 讨论(0)
  • 2020-12-01 05:50

    Now that everything has been said, don't break the semantics and create a new extension method:

      public static Boolean AllOrFalseIfEmpty<T>(this IEnumerable<T> source, Func<T, Boolean> predicate) {
         return source.Any() && source.All(predicate);
      }
    
    0 讨论(0)
提交回复
热议问题