为什么 IQueryable.All() 在空集合上返回 true?
所以我今天遇到了一种情况,一些生产代码失败正是因为一种方法完全按照 记录在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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
考虑以下陈述。
我声称
S1
暗示S2
。也就是说,语句S1 => S2 为真。我将通过证明它的否定是错误的来做到这一点。在这种情况下,
S1 => 的否定是S2
为S1 ^ ~S2
;这是因为S1 =>仅当
才为 false。S1
为 true 并且S2
为 false 时,S2S2
的否定是什么?这是S1 ^ ~S2
的真值是多少?我们把它写出来S1 ^ ~S2
为 true 的唯一方式是S1
和~S2
都为 true。但是S1
说我的车道是空的,而S2
说我的车道上有一辆车。我的车道不能既是空的又包含汽车。因此,S1
和~S2
不可能同时为真。因此,S1 ^ ~S2
为假,因此其否定S1 => S2 为真。
因此,如果您的车道是空的,您可以断言停在那里的所有汽车都是红色的。
现在让我们考虑一个
IEnumerable;元素
和一个Predicatep
。让我们假设elements
为空。我们希望发现 的值让我们考虑它的否定
如果
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
中的 allx
找不到 any,则谓词p
为 true > 这是错误的。但是,如果elements
中没有任何项目,则不可能找到任何为 false 的项目。因此,对于空集合elements
,p
对于elements
中的 allx
为 true >现在,当
elements
为空IEnumerable
且elements.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。最后,这里有一个相关解释,解释为什么
当
为 true:s
是string
的非空实例时,.StartsWith(String.Empty)Consider the following statements.
I claim that
S1
impliesS2
. That is, the statementS1 => S2
is true. I will do this by showing that its negation is false. In this case, the negation ofS1 => S2
isS1 ^ ~S2
; this is becauseS1 => S2
is false only whenS1
is true andS2
is false. What is the negation ofS2
? It isWhat is the truth value of
S1 ^ ~S2
? Let's write it outThe only way that
S1 ^ ~S2
can be true is if bothS1
and~S2
are true. ButS1
says that my driveway is empty andS2
says that there exists a car in my driveway. My driveway can not be both empty and contain a car. Thus, it is impossible forS1
and~S2
to both be true. Therefore,S1 ^ ~S2
is false so its negationS1 => 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 aPredicate<T> p
. Let us suppose thatelements
is empty. We wish to discover the value ofLet's consider its negation
For
notb
to be true, there must be at least onex
inelements
for which!p(x)
is true. Butelements
is empty so it is impossible to find anx
for which!p(x)
is true. Thereforenotb
can not be true so it must be false. Sincenotb
is false, its negation is true. Thereforeb
is true andelements.All(x => p(x))
must be true ifelements
is empty.Here's one more way to think of this. The predicate
p
is true if for allx
inelements
you can not find any for which it is false. But if there are no items inelements
then it is impossible to find any for which it is false. Thus, for an empty collectionelements
,p
is true for allx
inelements
Now, what about
elements.Any(x => p(x))
whenelements
is an emptyIEnumerable<T>
andp
is aPredicate<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. Forelements.Any(x => p(x))
to be true there must be at least onex
inelements
for whichp(x)
is true. But if there aren't anyx
inelements
it is impossible to find anyx
for whichp(x)
is true. Therefore,elements.Any(x => p(x))
is false ifelements
is empty.Finally, here's a related explanation on why
s.StartsWith(String.Empty)
is true whens
is a non-null instance ofstring
:如果返回
true
的项目数与所有项目的数量相同,则返回true
。就这么简单:相关解释: 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 returntrue
. Simple as that:Related explanation: Why does "abcd".StartsWith("") return true?
Any()
和All()
只是常用数学运算符 ∃(“存在限定符”或“存在”)和 ∀(“通用限定符”)的实现或“为所有人”)。“任何”意味着存在某个谓词为真的项目。对于空集合,这是错误的。
“全部”意味着不存在任何谓词为假的项目。对于空集合来说,这始终是正确的。
Any()
andAll()
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.
是的,可以。
为了证明我错了,请在你的空车道上展示一辆不是红色的汽车。
这是 SQL 的一个怪癖(并不完全正确:
NULL = NULL
和NULL <> NULL
都是未定义的,并且都不会匹配任何行。)Yes you can.
To prove me wrong, show me a car on your empty driveway that is not red.
This is a quirk of SQL (and not quite true:
NULL = NULL
andNULL <> NULL
are both undefined, and neither will match any rows.)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 ofAny(x => !x.Predicate)
("Are all cars red?" is the opposite of "Are there any cars that aren't red?").Any(x => !x.Predicate)
returnsfalse
for empty collections (which appears natural for the common understanding of "any").Hence
All(x => x.Predicate)
should (and does) returntrue
for empty collections.我认为这是有道理的。在逻辑上,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 likeAny()
.So
IQueryable.All()
is equivalent to!IQueryable.Any()
. If yourIQueryable
is empty, then both returns true based on MSDN doc.因为任何对空集的命题都是空洞的真理。
Because any proposition to an empty set would be a vacuous truth.
既然一切都已经说了,不要破坏语义并创建一个新的扩展方法:
Now that everything has been said, don't break the semantics and create a new extension method:
在数学或计算机科学的其他领域,您会经常发现这种行为。
如果范围无效(从 0 到 -1 的 SUM),数学中的 SUM 运算符将返回 0(+ 的中性元素)。 MULTIPYL 运算符将返回 1(乘法的中性元素)。
现在,如果您有布尔表达式,则非常相似:OR 的中性元素是
false
(a OR false = a
),而 AND 的中性元素是true
。现在,在 Linq 的
ANY
和ALL
上:它们与此类似:因此,如果您有数学/计算机背景,这种行为正是“您所期望的”。
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 istrue
.Now on Linq's
ANY
andALL
: They are similar to this:So this behavior is just what "you would expect" if you have a math/cs background.
返回
true
也是符合逻辑的。您有两个语句:“有车吗?” 和 “它是红色的吗?” 如果第一个语句为false
,则它不会无论第二条语句是什么,结果都是 modus ponenstrue >。Returning
true
is also logical. You have two statements: "Have a car?" and "Is it red?" If the first statement isfalse
, it doesn't matter what the second statement is, the result istrue
by modus ponens.它与数字零的基本概念非常相似。尽管它代表了缺席的存在,但它仍然具有并代表着价值。 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