所以我遇到了今天的情况,其中一些生产代码正在失败,因为一个方法完全像documented in MSDN那样执行。因为没有阅读文档而感到羞耻。但是,我仍然在为为什么它的行为方式而烦恼,即使是“按设计”,因为这种行为正好与我预期的那样(以及其他已知行为)和因此似乎违反了最不惊讶的原则。
All()
方法允许您提供谓词(例如lambda表达式)来测试IQueryable
,返回一个布尔值,指示所有集合成员是否与测试匹配。到现在为止还挺好。这就是它变得奇怪的地方。如果集合为空,All()
也会返回true
。由于以下原因,这似乎完全落后于我:
Any()
方法的行为符合预期,并且(正确)返回false,因为它没有任何与谓词匹配的成员。所以我的问题是,为什么All()
表现得这样?它解决了什么问题?这是否违反了最不意外的原则?
我将此问题标记为.NET 3.5,但该行为也适用于.NET 4.0。
编辑好的,所以我掌握了这方面的逻辑方面,正如杰森和其他人所做的那样出色。不可否认,空集合是一种边缘情况。我想我的问题根源于斗争,只是因为某些东西是逻辑并不意味着如果你没有处于正确的思维框架中它必然会使有意义。
答案 0 :(得分:42)
如果我的车道是空的,我不能断言停在那里的所有车都是红色的。
请考虑以下陈述。
S1
:我的车道是空的。
S2
:停在车道上的所有车都是红色的。
我声称S1
暗示S2
。也就是说,声明S1 => S2
是真的。我将通过表明其否定是错误来做到这一点。在这种情况下,S1 => S2
的否定是S1 ^ ~S2
;这是因为S1 => S2
仅在S1
为真且S2
为假时才为假。 S2
的否定是什么?它是
~S2
:车道上停着一辆不是红色的车。
S1 ^ ~S2
的真值是多少?我们把它写出来
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
成立,x
中必须至少有一个elements
!p(x)
为真。但elements
为空,因此无法找到x
为!p(x)
的{{1}}。因此notb
不可能是真的,所以它必须是假的。由于notb
是假的,它的否定是正确的。因此b
为真,如果elements.All(x => p(x))
为空,则elements
必须为真。
这是另一种思考方式。如果p
中的所有 x
您找不到任何的,则谓词elements
为真。但是如果elements
中没有项目,那么找不到任何是错误的。因此,对于空集elements
,p
对于x
elements
都是如此
现在,如果elements.Any(x => p(x))
为空elements
且IEnumerable<T>
为p
,那么Predicate<T>
如何?我们已经知道结果将是错误的,因为我们知道它的否定是正确的,但无论如何我们都要通过它来推理它。直觉很有价值。要使elements.Any(x => p(x))
为真,x
中必须至少有一个elements
p(x)
为真。但如果x
中没有任何 elements
,就无法找到{em>任何 x
p(x)
是真的。因此,如果elements.Any(x => p(x))
为空,则elements
为false。
最后,当s.StartsWith(String.Empty)
是s
的非空实例时,string
为{{1}}为真,这是related explanation:
答案 1 :(得分:7)
如果返回true
的项目数与所有项目的数量相同,则返回true
。很简单:
Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()
答案 2 :(得分:4)
“如果集合为空,则进行测试 像这样,充其量是未定义的。如果 我的车道是空的,我无法断言 停在那里的所有车都是红色的。“
是的,你可以。
为了证明我的错,请在空车道上给我看一辆不是红色的车。
对于熟悉SQL概念的人来说,NULL!= NULL,这是意外行为。
这是SQL的一个怪癖(并不完全正确:NULL = NULL
和NULL <> NULL
都是未定义的,并且都不匹配任何行。)
答案 3 :(得分:3)
我认为这是有道理的。在逻辑上,FOR ALL的补充不是(存在)。 FOR ALL就像All()
。 EXERE就像Any()
。
因此IQueryable.All()
相当于!IQueryable.Any()
。如果您的IQueryable
为空,则两者都会根据MSDN文档返回true。
答案 4 :(得分:3)
Any()
和All()
只是通常的数学运算符∃(“存在的量子”或“存在”)和∀(“通用量子”或“为所有人”)的实现。
“任何”表示存在谓词为真的某个项目。对于空集合,这将是错误的。
“全部”表示不存在谓词为假的任何项目。对于空集合,这总是如此。
答案 5 :(得分:1)
返回true
也是合乎逻辑的。你有两个陈述:“有车?”和“它是红色的吗?”如果第一个陈述是false
,那么无关紧要第二个陈述是,结果是true
modus ponens。
答案 6 :(得分:1)
你会在数学或计算机科学的其他领域经常发现这种行为。
如果范围无效(SUM从0到-1),Math中的SUM运算符将返回0(+的中性元素)。 MULTIPYL运算符将返回1(乘法的中性元素)。
现在,如果你有布尔表达式,它就非常相似:OR的中性元素是false
(a OR false = a
),而AND的中性元素是true
。
现在Linq的ANY
和ALL
:他们与此类似:
ANY = a OR b OR c OR d ...
ALL = a AND b AND c AND d ...
因此,如果您有数学/ cs背景,这种行为正是“您所期望的”。
答案 7 :(得分:1)
因为对空集的任何命题都是vacuous truth。
答案 8 :(得分:1)
现在已经说了一切,请不要破坏语义并创建新的扩展方法:
public static Boolean AllOrFalseIfEmpty<T>(this IEnumerable<T> source, Func<T, Boolean> predicate) {
return source.Any() && source.All(predicate);
}
答案 9 :(得分:0)
False意味着即使没有谓词,查询也不会返回任何结果。这只是一个不完整的方式来说明Dested在我输入时发布的内容。
答案 10 :(得分:0)
这与数字零的基本概念非常相似。即使它代表缺席的存在,它仍然拥有并代表一种价值。 IQueryable.All()应返回true,因为它将成功返回集合的所有成员。恰好如果集合为空,该函数将不会返回任何成员,但不会因为函数无法返回任何成员。这只是因为没有成员返回。话虽这么说,为什么IQueryable.All()由于缺乏集合的支持而必须经历失败?它愿意,它能够......它有能力。听起来像收藏品无法阻止他们讨价还价的结束......
答案 11 :(得分:0)
All(x => x.Predicate)
与Any(x => !x.Predicate)
相反(&#34;所有车辆都是红色的?&#34;与#34相反;是否有任何车辆都没有红色?&#34;。)
Any(x => !x.Predicate)
会为空集合返回false
(这对于#34;任何&#34;的共同理解显得很自然。)
因此All(x => x.Predicate)
应该(并且确实)为空集合返回true
。