为什么 IQueryable.All() 在空集合上返回 true?

发布于 2024-08-20 12:42:09 字数 904 浏览 7 评论 0原文

所以我今天遇到了一种情况,一些生产代码失败正是因为一种方法完全按照 记录在MSDN中。为我没有阅读文档而感到羞耻。然而,我仍然摸不着头脑为什么它会这样,即使是“设计使然”,因为这种行为与我的预期(以及其他已知的行为)完全相反,并且因此似乎违反了最小意外原则。

All() 方法允许您提供谓词(例如 lambda 表达式)来测试 IQueryable,返回一个布尔值,指示所有集合成员是否与测试匹配。到目前为止,一切都很好。这就是奇怪的地方。如果集合为空,All() 也会返回 true。这对我来说似乎完全倒退,原因如下:

  • 如果集合是空的,这样的测试充其量是未定义的。如果我的车道是空的,我不能断言停在那里的所有汽车都是红色的。通过这种行为,在空车道上停放的所有汽车都是红色、蓝色和棋盘 - 所有这些表达式都将返回 true。
  • 对于任何熟悉 NULL != NULL SQL 概念的人来说,这是意想不到的行为。
  • Any() 方法的行为符合预期,并且(正确地)返回 false,因为它没有任何与谓词匹配的成员。

所以我的问题是,为什么 All() 会这样?它解决什么问题?这是否违反了最小意外原则?

我将此问题标记为 .NET 3.5,尽管该行为也适用于 .NET 4.0。

编辑 好的,我掌握了这一点的逻辑方面,正如杰森和你们其他人出色地阐述的那样。诚然,空集合是一种边缘情况。我想我的问题源于这样的斗争:如果你的心态不正确,仅仅因为某件事是合乎逻辑的并不意味着它一定有意义。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

十级心震 2024-08-27 12:42:09

如果我的车道是空的,我不能断言停在那里的所有汽车都是红色的。

考虑以下陈述。

S1:我的车道是空的。

S2:停在我车道上的所有汽车都是红色的。

我声称 S1 暗示 S2。也就是说,语句 S1 => S2 为真。我将通过证明它的否定是错误的来做到这一点。在这种情况下,S1 => 的否定是S2S1 ^ ~S2;这是因为 S1 =>仅当 S1 为 true 并且 S2 为 false 时,S2 才为 false。 S2 的否定是什么?这是

~S2:我的车道上停着一辆不是红色的汽车。

S1 ^ ~S2 的真值是多少?我们把它写出来

S1 ^ ~S2:我的车道是空的,并且我的车道上停着一辆不是红色的汽车。

S1 ^ ~S2 为 true 的唯一方式是 S1~S2 都为 true。但是 S1 说我的车道是空的,而 S2 说我的车道上有一辆车。我的车道不能既是空的又包含汽车。因此,S1~S2 不可能同时为真。因此,S1 ^ ~S2 为假,因此其否定 S1 => S2 为真。

因此,如果您的车道是空的,您可以断言停在那里的所有汽车都是红色的。

现在让我们考虑一个 IEnumerable;元素和一个Predicatep。让我们假设 elements 为空。我们希望发现 的值

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

让我们考虑它的否定

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

如果 notb 为真,则 elements 中必须至少有一个 x ,其中 < code>!p(x) 为 true。但是 elements 是空的,因此不可能找到 !p(x) 为 true 的 x 。因此 notb 不可能为 true,因此它一定为 false。由于 notb 为假,因此其否定为真。因此,如果 elements 为空,b 为 true,并且 elements.All(x => p(x)) 也必须为 true。

这是思考这个问题的另一种方式。如果对于 elements 中的 all x 找不到 any,则谓词 p 为 true > 这是错误的。但是,如果 elements 中没有任何项目,则不可能找到任何为 false 的项目。因此,对于空集合 elementsp 对于 elements 中的 all x 为 true >

现在,当 elements 为空 IEnumerableelements.Any(x => p(x)) 时,会怎么样? code>p 是上面的 Predicate 吗?我们已经知道结果将是假的,因为我们知道它的否定是真的,但无论如何让我们通过它进行推理;直觉是有价值的。要使 elements.Any(x => p(x)) 为 true,elements 中必须至少有一个 x,其中 <代码>p(x)为真。但是,如果 elements 中没有任何 x,则不可能找到任何 x 其中 p(x) 为真。因此,如果 elements 为空,则 elements.Any(x => p(x)) 为 false。

最后,这里有一个相关解释,解释为什么sstring 的非空实例时,.StartsWith(String.Empty) 为 true:

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:

我的痛♀有谁懂 2024-08-27 12:42:09

如果返回true的项目数与所有项目的数量相同,则返回true。就这么简单:

Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()

相关解释: Why does "abcd".StartsWith("") return true ?

If the number of the items that return true is the same as the number of all the items, then return true. Simple as that:

Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()

Related explanation: Why does "abcd".StartsWith("") return true?

孤寂小茶 2024-08-27 12:42:09

Any()All() 只是常用数学运算符 ∃(“存在限定符”或“存在”)和 ∀(“通用限定符”)的实现或“为所有人”)。

“任何”意味着存在某个谓词为真的项目。对于空集合,这是错误的。

“全部”意味着不存在任何谓词为假的项目。对于空集合来说,这始终是正确的。

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.

黑白记忆 2024-08-27 12:42:09

“如果集合为空,则进行测试
就像这样,充其量是未定义的。如果
我的车道是空的,我无法断言
所有停在那里的汽车都是红色的。”

是的,可以。

为了证明我错了,请在你的空车道上展示一辆不是红色的汽车。

对于任何熟悉 NULL != NULL SQL 概念的人来说,这是意外的行为。

这是 SQL 的一个怪癖(并不完全正确:NULL = NULLNULL <> NULL 都是未定义的,并且都不会匹配任何行。)

"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."

Yes you can.

To prove me wrong, show me a car on your empty driveway that is not red.

For anyone familiar with the SQL notion that NULL != NULL, this is unexpected behavior.

This is a quirk of SQL (and not quite true: NULL = NULL and NULL <> NULL are both undefined, and neither will match any rows.)

三生池水覆流年 2024-08-27 12:42:09

All(x => x.Predicate)Any(x => !x.Predicate) 相反(“Are all cars red?” 是相反的“有没有不是红色的汽车?”)。

Any(x => !x.Predicate) 对于空集合返回 false(这对于“any”的普遍理解来说是很自然的)。

因此,对于空集合,All(x => x.Predicate) 应该(并且确实)返回 true

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.

原谅过去的我 2024-08-27 12:42:09

我认为这是有道理的。在逻辑上,FOR ALL 的补码是 NOT (THERE EXIST)。 FOR ALL 就像All()。 THERE EXIST 就像Any()

因此 IQueryable.All() 相当于 !IQueryable.Any()。如果您的 IQueryable 为空,则根据 MSDN 文档,两者都会返回 true。

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.

心意如水 2024-08-27 12:42:09

因为任何对空集的命题都是空洞的真理

Because any proposition to an empty set would be a vacuous truth.

孤寂小茶 2024-08-27 12:42:09

既然一切都已经说了,不要破坏语义并创建一个新的扩展方法:

  public static Boolean AllOrFalseIfEmpty<T>(this IEnumerable<T> source, Func<T, Boolean> predicate) {
     return source.Any() && source.All(predicate);
  }

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);
  }
七色彩虹 2024-08-27 12:42:09

在数学或计算机科学的其他领域,您会经常发现这种行为。

如果范围无效(从 0 到 -1 的 SUM),数学中的 SUM 运算符将返回 0(+ 的中性元素)。 MULTIPYL 运算符将返回 1(乘法的中性元素)。

现在,如果您有布尔表达式,则非常相似:OR 的中性元素是 false (a OR false = a),而 AND 的中性元素是 true

现在,在 Linq 的 ANYALL 上:它们与此类似:

ANY = a OR b OR c OR d ...
ALL = a AND b AND c AND d ...

因此,如果您有数学/计算机背景,这种行为正是“您所期望的”。

You will find this behaviour quite often in other areas of mathematics or computer science.

The SUM operator in Math will return 0 (the neutral element of +) in cases where the ranges are invalid (the SUM from 0 up to -1). The MULTIPYL operator will return 1 (neutral element for multiplication).

Now if you have Boolean expressions, it's quite similar: The neutral element for OR is false (a OR false = a) whereas the neutral element for AND is true.

Now on Linq's ANY and ALL: They are similar to this:

ANY = a OR b OR c OR d ...
ALL = a AND b AND c AND d ...

So this behavior is just what "you would expect" if you have a math/cs background.

药祭#氼 2024-08-27 12:42:09

返回 true 也是符合逻辑的。您有两个语句:“有车吗?”“它是红色的吗?” 如果第一个语句为 false,则它不会无论第二条语句是什么,结果都是 modus ponenstrue >。

Returning true is also logical. You have two statements: "Have a car?" and "Is it red?" If the first statement is false, it doesn't matter what the second statement is, the result is true by modus ponens.

新雨望断虹 2024-08-27 12:42:09

它与数字零的基本概念非常相似。尽管它代表了缺席的存在,但它仍然具有并代表着价值。 IQueryable.All() 应该返回 true,因为它将成功返回集合的所有成员。碰巧的是,如果集合为空,函数将不会返回任何成员,但这并不是因为函数无法返回任何成员。只是因为没有成员回来。话虽这么说,为什么 IQueryable.All() 由于缺乏集合的支持而必须经历失败?它愿意,它能够……它有能力。在我看来,这个系列无法兑现他们的承诺......

链接

It's very similar to the basic concept of the number zero. Even though it represents the existence of absence, it still possesses and represents a value. IQueryable.All() should return true, because it will successfully return all of the members of the collection. It just so happens that if the collection is empty, the function won't return any members, but not because the function couldn't return any members. It was only because there were no members to return. That being said, why should IQueryable.All() have to experience failure due to the lack of support from the collection? It was willing, it was able...it was capable. Sounds to me like the collection couldn't hold up their end of the bargain...

Link

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文