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

所以今天遇到了一些情况,一些生产代码不能正确地执行,因为一个方法完全按照MSDN的规定执行 。 因为我没有阅读文档而感到羞耻。 然而,即使“devise上”,我仍然在为自己的行为而挠头,因为这种行为与我期望的(和其他已知的行为)完全相反,因此似乎违反了原则最less的惊喜。

All()方法允许您提供谓词(如lambdaexpression式)来testingIQueryable ,并返回一个布尔值,指示所有集合成员是否匹配testing。 到现在为止还挺好。 这是奇怪的地方。 如果集合是空的, All()也返回true 。 这似乎完全倒退给我,原因如下:

  • 如果这个集合是空的,像这样的testing最好是未定义的。 如果我的车道是空的,我不能断言所有停在那里的车都是红色的。 有了这个行为,在一个空的车道上,所有停在那里的车都是红色的,蓝色的和棋盘格 – 所有这些expression式都会返回true。
  • 对于熟悉SQL!的人来说,NULL!= NULL,这是意料之外的行为。
  • Any()方法的行为与预期相同,并且(正确)返回false,因为它没有任何与谓词相匹配的成员。

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

我把这个问题标记为.NET 3.5,但是这个行为也适用于.NET 4.0。

编辑好的,所以我把握这个逻辑层面,就像杰森和你其他人一样。 诚然,一个空的收集是一个边缘的情况下。 我想我的问题是根植于这样一种斗争,即只是因为某些东西是合乎逻辑的 ,并不意味着如果你不在正确的思维范围内,这是有道理的。

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

考虑下面的陈述。

S1 :我的车道是空的。

S2 :停在我车道的车都是红色的。

我声称S1意味着S2 。 就是说,语句S1 => S2是真的。 我会通过表明它的否定是假的来做到这一点。 在这种情况下, S1 => S2的否定是S1 ^ ~S2 ; 这是因为S1 => S2仅当S1为真且S2为假时才是错误的。 S2的否定是什么? 它是

~S2 :车道上停放的车辆不是红色的。

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

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

S1 ^ ~S2可以是真的唯一方法是S1~S2都是真的。 但S1说我的车道是空的, S2说在我的车道上有一辆车。 我的车道不能都是空的,并包含一辆车。 因此, S1~S2都不可能是正确的。 因此, S1 ^ ~S2是错误的,所以它的否定S1 => S2是真的。

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

所以现在让我们考虑一个IEnumerable<T> elements和一个Predicate<T> p 。 让我们假设elements是空的。 我们希望发现的价值

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

让我们考虑它的否定

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

对于notb是真实的,在!p(x)为真的elements必须至less有一个x 。 但是elements是空的,所以不可能find一个x!p(x)是真的。 所以notb不可能是真的所以它一定是假的。 由于notb是假的,它的否定是真实的。 因此,如果elements为空,则b为真,并且elements.All(x => p(x))必须为真。

这里有更多的方法来思考这个问题。 谓词p是真的,如果对于所有 x中的elements你不能find它是错误的。 但是,如果elements中没有项目,那么就不可能find任何错误的东西。 因此,对于一个空的集合elementsp对于所有elements都是真的

那么elements.Any(x => p(x))什么elements是一个空的IEnumerable<T>p是一个Predicate<T> ,如上所示? 我们已经知道结果是错误的,因为我们知道它的否定是真实的,但无论如何我们还是要通过它来推理。 直觉是有价值的。 对于elements.Any(x => p(x))是真的,在p(x)为真的elements必须至less有一个x 。 但是,如果elements中没有任何 x ,就不可能findp(x)为真的任何 x 。 因此,如果elements为空,那么elements.Any(x => p(x))为false。

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

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

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

相关解释: 为什么“abcd”.StartsWith(“”)返回true?

“如果收集是空的,像这样的testing充其量是不确定的,如果我的车道是空的,我不能断言所有停在那里的车都是红色的。

是的你可以。

为了certificate我错了,在空荡的车道上给我一辆不是红色的车。

对于熟悉SQL!的人来说,NULL!= NULL,这是意料之外的行为。

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

Any()All()只是通常的math操作符(“存在的分词”或“存在”)和∀(“通用分词”或“全部”)的实现。

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

“全部”意味着不存在任何谓词错误的项目。 对于空集合,这将永远是真实的。

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

所以IQueryable.All()相当于!IQueryable.Any() 。 如果你的IQueryable是空的,那么两个都会根据MSDN doc返回true。

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

在范围无效的情况下(SUM从0到-1),Math中的SUM运算符将返回0(+的中性元素)。 MULTIPYL运算符将返回1(乘法的中立元素)。

现在如果你有布尔expression式,它是非常相似的:OR的中性元素是falsea OR false = a ),而中性的元素为true

现在Linq的ANYALL都是这样的:

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

所以如果你有一个math/ cs背景,这个行为就是“你会期望的”。

回归true也是合乎逻辑的。 你有两个陈述: “有车吗?”“红色?” 如果第一个陈述是false ,那第二个陈述是什么并不重要,结果通过modus ponens是true

因为任何提议都是空洞的事实 。

False意味着即使没有谓词,查询也不会返回任何结果。 这只是一个不完整的方式,说明什么Dested张贴,因为我input这个。

这与数字零的基本概念非常相似。 即使它代表了缺席的存在,它仍然具有和代表一种价值。 IQueryable.All()应该返回true,因为它将成功返回集合的所有成员。 恰好如此,如果集合是空的,函数不会返回任何成员,但不是因为该函数不能返回任何成员。 只是因为没有成员回来。 这就是说,为什么IQueryable.All()必须经历失败,因为缺乏集合的支持? 它是愿意的,它能够…它是有能力的。 听起来像collections不能阻止他们讨价还价的结束…

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

All(x => x.Predicate)Any(x => !x.Predicate)相反Any(x => !x.Predicate) (“所有车辆都是红色的?”与“是否有车辆不是红色的?

Any(x => !x.Predicate)对于空集合都返回false (对于“any”的共同理解,这看起来很自然)。

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