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

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

所以我今天遇到了这样一种情况:一些生产代码之所以失败,正是因为有一种方法的执行与msdn中所记录的完全相同。我很遗憾没有阅读文档。然而,我仍然在绞尽脑汁地思考它为什么会这样做,即使是"按设计",因为这种行为与我预期的完全相反(以及其他已知的行为),因此似乎违反了最不令人惊讶的原则。

All()方法允许您提供一个谓词(例如lambda表达式)来测试IQueryable,返回一个布尔值,该值指示所有集合成员是否与测试匹配。到现在为止,一直都还不错。这就是奇怪的地方。如果集合为空,All()也返回true。在我看来,这完全是倒退的,原因如下:

  • 如果集合为空,那么这样的测试最多是未定义的。如果我的车道是空的,我不能断言所有停在那里的车都是红色的。有了这种行为,在一条空车道上,所有停在那里的车都有红蓝相间的棋盘格——所有这些表达式都会返回真值。
  • 对于任何熟悉SQL概念的人来说,这是空的!=空,这是意外行为。
  • Any()方法的行为与预期一致,并且(正确地)返回false,因为它没有任何与谓词匹配的成员。

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

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

编辑好了,所以我掌握了这方面的逻辑,就像杰森和你们其他人所做的那样。诚然,一个空的集合有点像一个边缘案例。我想我的问题根源于这场斗争,仅仅因为某些事情是合乎逻辑的,并不意味着如果你没有处于正确的心态中,这就必然是有意义的。


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

考虑以下陈述。

S1: My driveway is empty.

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

我声称S1表示S2。也就是说,S1 => S2的说法是正确的。我会证明它的否定是错误的。在这种情况下,S1 => S2的否定是S1 ^ ~S2;这是因为只有当S1为真,S2为假时,S1 => S2才是假的。S2的否定是什么?它是

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

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

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

只有当S1~S2都是真的,S1 ^ ~S2才是真的。但是S1说我的车道是空的,S2说我的车道上有一辆车。我的车道不能既空又有车。因此,S1~S2都不可能是真的。因此,S1 ^ ~S2是假的,所以它的否定S1 => S2是真的。

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

现在让我们考虑一个IEnumerable elements和一个Predicate p。假设elements是空的。我们希望发现

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

让我们考虑一下它的否定

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

为了使notb为真,elements中必须至少有一个x!p(x)为真。但是elements是空的,所以不可能找到一个x是真的。因此,notb不能是真的,所以它一定是假的。由于notb是假的,它的否定是真的。因此,如果elements为空,b为真,elements.All(x => p(x))为真。

还有一种方法可以考虑这个问题。如果在elements中的所有x找不到任何虚假的,则谓词p为真。但如果elements中没有项目,就不可能找到任何虚假项目。因此,对于一个空的集合elementsp对于elements中的所有x都是正确的。

现在,当elements是一个空的IEnumerablep是一个如上所述的Predicate时,elements.Any(x => p(x))怎么办?我们已经知道结果是错误的,因为我们知道它的否定是正确的,但是让我们通过它来推理,直觉是有价值的。为了使elements.Any(x => p(x))为真,elements中必须至少有一个xp(x)为真。但是如果elements中没有x的话,就不可能找到p(x)是真的x。因此,如果elements为空,则elements.Any(x => p(x))为假。

最后,本文对当sstring的非空实例时,为什么s.StartsWith(String.Empty)是真的作了相关解释:


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

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

相关说明:为什么"abcd".startswith(")返回true?


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

是的,你可以。

为了证明我错了,让我看看你空车道上一辆不是红色的车。

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

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


Any()All()只是普通数学运算符的实现?(即"存在的四元证明者"或"存在的四元证明者")和?(以下简称"通用quartifier"或"for all")。

"any"表示存在谓词为真的项。对于空集合,这将是错误的。

"全部"表示不存在谓词为假的项。对于空集合,这始终是正确的。


我认为这是有道理的。在逻辑上,对所有事物的补充是不存在的。因为一切都像江户记1(7)。存在着类似于Any()

因此,IQueryable.All()相当于!IQueryable.Any()。如果您的IQueryable为空,那么根据msdn-doc,两者都返回true。


因为对一个空集合的任何命题都是一个空洞的真理。


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

如果范围无效(从0到-1的总和),Math中的sum运算符将返回0(零元素+)。multipyl运算符将返回1(用于乘法的中性元素)。

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

现在关于Linq的ANYALL:它们类似于:

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

所以,如果你有数学/cs背景,这种行为就是"你所期望的"。


返回true也是合乎逻辑的。你有两句话:"有车吗?""它是红色的吗?"如果第一个语句是false,那么第二个语句是什么并不重要,结果是modus ponens生成的true


All(x => x.Predicate)Any(x => !x.Predicate)的对立面("所有汽车都是红色的吗?"与"有没有不红色的车?"相反吗?.

Any(x => !x.Predicate)返回false用于空集合(对于"any"的共同理解,这似乎是自然的)。

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


它非常类似于数字0的基本概念。即使它代表着缺席的存在,它仍然拥有并代表着一个价值。iqueryable.all()应返回true,因为它将成功返回集合的所有成员。如果集合为空,则函数不会返回任何成员,但不会返回任何成员,因为函数无法返回任何成员。这仅仅是因为没有成员可以返回。也就是说,为什么iqueryable.all()必须经历失败,因为缺乏集合的支持?它是自愿的,它是有能力的……它是有能力的。在我看来,这些收藏品没法维持他们的交易…

http://mathforum.org/dr.math/faq/faq.divideby0.html


false表示查询没有返回任何结果,即使没有谓词。这只是一个不完整的方式来说明什么目的地张贴当我打字这个。