为什么Enumerable.All为空序列返回true?

Cui*_*崔鹏飞 100 .net c# linq enumerable

var strs = new Collection<string>();
bool b = strs.All(str => str == "ABC");
Run Code Online (Sandbox Code Playgroud)

代码创建一个空的字符串集合,然后尝试确定集合中的所有元素是否为"ABC".如果你运行它,b将是真的.

但是该集合甚至没有任何元素,更不用说任何等于"ABC"的元素了.

这是一个错误,还是有合理的解释?

Jon*_*eet 151

这肯定不是一个bug.它的行为完全符合记录:

如果源序列的每个元素都通过指定谓词中的测试,或者序列为空,则返回 true;否则返回false .否则,错误.

现在你可以争论它是否应该以这种方式工作(对我来说似乎很好;序列的每个元素都符合谓词)但是在你询问某些东西是否是一个bug之前首先要检查的是文档.(只要方法的行为与您预期的方式不同,这是首先检查的.)

  • 如果没有这个属性,`!Any(Predicate)`将不会与`All(!Predicate)`相同,这将是*非常不直观的. (53认同)
  • 另一张海报我发现这种情况被称为[空洞真相](http://en.wikipedia.org/wiki/Vacuous_truth) (24认同)
  • @ Stimul8d:只有你根本没有任何银行账户(或者如果你*拥有银行账户,并且所有银行账户中都有这么多账户),这是真的. (8认同)
  • 实际上,搜索中的顶级SO链接是我在函数不按预期运行时检查的第一件事.总是像Jon Skeet这样拥有巨大声誉的人不仅找到了相关的文档片段,而且还解释了为什么我的期望从一开始就是错误的.我们被SO宠坏了...... (7认同)
  • 我不是说这是一个错误,但它很奇怪.我的任何银行账户都没有钱,所以BankAccounts.All(x => x.TotalFunds> 1000000)是真的??? 我要回家了! (5认同)
  • @JonSkeet该死的,......回去工作!:-) (2认同)
  • 这毫无意义.给定一个空集,有一个直面的人如何说"所有"匹配一个条件实际上"没有"他们做...因为没有?具体来说,All和Any不是逻辑反转,所以!任何(谓词)不应该与All(!Predicate)相同.假设您要声明用户权限的所有特定子集都满足最低访问级别.您无法使用"all> = minAccess",因为如果没有权限,它将返回true.您也可以不使用"不是任何<minAccess",因为对于空集也会返回true. (2认同)
  • @Triynko:当谈到它是否与空集相匹配时,人们基本上会以不同的方式理解"所有"这两个词.你*声明*All和Any不是逻辑反转,但我是这样看的 - LINQ设计师也是如此.有时候当前的行为是痛苦的,同样有时候你提出的行为很痛苦. (2认同)
  • 我不同意。将“全部”和“任意”作为逻辑倒数实际上会产生这个精确的问题。首先,通过将它们等同起来,你就使它们变得多余。相反,如果“All”返回“false”(对于空集,它应该如此),那么当非空集是前提条件时,您将使用“All”,而当非空集不是前提条件时,您可以使用“not any” 。所以我提议的行为不会很痛苦,因为你有一个实际的替代方法。目前,如果我将复杂的 lambda 传递到 where...,我不能简单地调用“All”,也不能调用“Not Any”。我必须重复 lambda 并调用 Any 和 All。 (2认同)
  • 比较 MSDN 上作为注释添加的悖论:所有项目都与谓词匹配,因为没有项目与谓词匹配。这不符合逻辑,也没有习惯,甚至法律也不能让它符合逻辑。 (2认同)

Mr.*_*tty 10

All要求谓词对序列的所有元素都是真的.这在文档中明确说明.如果你认为All是一个逻辑的,并且在谓词的每个元素的结果之间,这也是唯一有意义的事情.你得到的空序列的"真实"是和操作的标识元素.同样,对于空序列,您从Any获得的错误是逻辑或逻辑的标识.

如果你认为"所有"是"序列中没有元素没有",那么这可能更有意义.

  • "序列中没有元素没有",这是一个很好的元素 (3认同)
  • 这是迄今为止最好的答案。当设计这些方法时,考虑了数学背景。感谢您指出这一点,而不仅仅是像其他答案一样一遍又一遍地声明“它是在文档中写的”! (2认同)

lep*_*pie 8

它是true,因为没有(没有条件)成功false.

文档可能会解释它.(Jon Skeet几年前也提到了一些事情)

这同样适用于Any(的对面All返回)false为空集.

编辑:

您可以想象All在语义上实现与以下相同:

foreach (var e in elems)
{
  if (!cond(e))
    return false;
}
return true; // no escape from loop
Run Code Online (Sandbox Code Playgroud)


Jua*_*uan 6

这里的大多数答案似乎都遵循“因为这就是定义的方式”。但也有一个合乎逻辑的原因,为什么这样定义。

定义函数时,您希望函数尽可能通用,以便它可以应用于尽可能多的情况。例如,假设我想定义一个Sum函数,它返回列表中所有数字的总和。当列表为空时它应该返回什么?如果您要返回一个任意 number x,则将该函数定义为:

  1. 返回给定列表中所有数字总和的函数,或者x如果列表为空。

但如果x为零,您也可以将其定义为

  1. 返回x加上给定数字的函数。

请注意,定义 2 暗示了定义 1,但 1 在x不为零时并不暗示 2 ,这本身就是选择 2 而非 1 的充分理由。但也注意 2更优雅,并且就其本身而言,比 1 更通用。就像将聚光灯放在更远的地方,以便照亮更大的区域。实际要大很多。我自己不是数学家,但我相信他们会发现定义 2 和其他数学概念之间的大量联系,但与定义 1 相关的x不是零。

一般来说,只要有一个函数对一组元素应用二元运算符并且该集合为空,就可以并且很可能希望返回标识元素(使另一个操作数保持不变的元素)。这与Product当列表为空时函数将返回 1 的原因相同(请注意,您可以x在定义 2 中将“加号”替换为“一次”)。And is the same reason All(可以认为是逻辑AND运算符的重复应用)true在列表为空时会返回(p && true相当于p),同样的原因Any(OR运算符)会返回false