为什么 Enumerable.All 对于空序列返回 true
本文关键字:返回 true 于空序 Enumerable All 为什么 | 更新日期: 2023-09-27 18:12:31
var strs = new Collection<string>();
bool b = strs.All(str => str == "ABC");
该代码创建一个空的字符串集合,然后尝试确定集合中的所有元素是否都是"ABC"。如果你运行它,b
将是真的。
但是该集合中甚至没有任何元素,更不用说任何等于" ABC"的元素了。
这是一个错误,还是有合理的解释?
这当然不是错误。它的行为与记录的完全一样:
如果源序列的每个元素都通过指定谓词中的测试,或者序列为空,则为 true;否则为 false。
现在你可以争论它是否应该以这种方式工作(对我来说似乎很好;序列的每个元素都符合谓词(,但在你问某事是否是错误之前,首先要检查的是文档。(这是方法行为方式与预期不同时立即检查的第一件事。
All
要求谓词对于序列的所有元素都为 true。 这在文档中明确说明。如果您认为All
就像每个元素的谓词结果之间的逻辑"和",这也是唯一有意义的事情。 您为空序列输出true
是"and"操作的标识元素。 同样,您从空序列的Any
获得的false
是逻辑"or"的恒等式。
如果您将All
视为"序列中没有不是的元素",这可能更有意义。
它是true
的,因为没有什么(没有条件(使它false
。
文档可能会解释它。(乔恩·斯基特几年前也提到了一些事情(
Any
也是如此(与All
相反(返回空集的false
。
编辑:
您可以想象All
在语义上实现与以下内容相同:
foreach (var e in elems)
{
if (!cond(e))
return false;
}
return true; // no escape from loop
这里的大多数答案似乎都遵循"因为这就是定义的方式"。但是,以这种方式定义也有一个合乎逻辑的原因。
定义函数时,您希望函数尽可能通用,以便它可以应用于尽可能多的事例。例如,假设我想定义 Sum
函数,它返回列表中所有数字的总和。当列表为空时,它应该返回什么?如果返回任意数x
,则将函数定义为:
- 返回
- 给定列表中所有数字的总和的函数,如果列表为空,则返回
x
。
但如果x
为零,您也可以将其定义为
- 返回
x
加上给定数字的函数。
意味着定义 1,但当 x
不为零时,1 并不意味着 2,这本身就足以成为选择 2 而不是 1 的理由。但注释 2 也比 1 更优雅,而且本身更通用。就像将聚光灯放在更远的地方,以便照亮更大的区域。实际上要大得多。我自己不是数学家,但我相信他们会在定义 2 和其他数学概念之间找到大量联系,但当x
定义 1 不为零时,与定义 1 相关的联系并不多。
通常,只要您有一个函数对一组元素应用二元运算符并且该元素为空,您就可以并且很可能希望返回标识元素(保持另一个操作数不变的那个(。这与列表为空时 Product
函数返回 1 的原因相同(请注意,您可以在定义 2 中将"x
plus"替换为"一次"(。并且与All
(可以认为是逻辑 AND 运算符的重复应用(在列表为空时返回 true
(p && true
等效于 p
(的原因相同,Any
(OR 运算符(返回 false
的原因相同。
该方法循环遍历所有元素,直到找到不满足条件的元素,或者找不到任何失败的元素。如果没有失败,则返回 true。
因此,如果没有元素,则返回 true(因为没有失败的元素(
这是一个可以做OP想要做的事情的扩展:
static bool All<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool mustExist)
{
foreach (var e in source)
{
if (!predicate(e))
return false;
mustExist = false;
}
return !mustExist;
}
。正如其他人已经指出的那样,这不是一个错误,而是有据可查的预期行为。
如果不想编写新的扩展,另一种解决方案是:
strs.DefaultIfEmpty().All(str => str == "ABC");
PS:如果查找默认值本身,则上述内容不起作用!(对于字符串,这将为空。在这种情况下,它变得不那么优雅,类似于:
strs.DefaultIfEmpty(string.Empty).All(str => str == null);
如果可以枚举多次,最简单的解决方案是:
strs.All(predicate) && strs.Any();
即只需添加检查,之后实际上有任何元素。
将实现放在一边。真的重要吗?查看您是否有一些代码可以迭代枚举并执行一些代码。如果 All(( 为 true,则该代码仍然不会运行,因为枚举中没有任何元素。
var hungryDogs = Enumerable.Empty<Dog>();
bool allAreHungry = hungryDogs.All(d=>d.Hungry);
if (allAreHungry)
foreach (Dog dog in hungryDogs)
dog.Feed(biscuits); <--- this line will not run anyway.