为什么Enumerable.All对于一个空序列返回true?
var strs = new Collection<string>(); bool b = strs.All(str => str == "ABC");
代码创build一个空的string集合,然后尝试确定集合中的所有元素是否为“ABC”。 如果你运行它, b
将是真实的。
但collections甚至没有任何元素,更不用说任何等于“ABC”的元素。
这是一个错误,还是有一个合理的解释?
这当然不是一个错误。 它的行为完全如文件所述 :
如果源序列的每个元素都通过指定谓词中的testing, 或者序列为空 , 则为 true; 否则,是错误的。
现在你可以争论它是否应该以这种方式工作(对我来说这似乎很好,序列中的每个元素都符合谓词),但是在你询问是否有错误之前首先要检查的是文档。 (只要一个方法的行为与你所期望的不同,就要首先检查它。)
所有这些都要求谓词对于序列的所有元素都是正确的。 这在文档中有明确说明。 如果你把所有的东西都看作是一个逻辑关系和每个元素的谓词结果之间的关系,那么这也是唯一有意义的东西。 你正在为空序列获得的“真实”是和操作的标识元素。 同样,你从任何空的序列得到的错误是逻辑或的身份。
如果你认为“全部”是“序列中没有元素不是”,这可能更有意义。
true
,因为没有任何东西(没有条件)使它成为false
。
文件可能解释它。 (Jon Skeet在几年前也提到了一些事情)
Any
(与All
相反)都为空集合返回false
。
编辑:
你可以想象All
语义实现都是一样的:
foreach (var e in elems) { if (!cond(e)) return false; } return true; // no escape from loop
该方法遍历所有元素,直到find不满足条件的元素,或找不到任何失败的元素。 如果没有失败,则返回true。
所以,如果没有元素,返回true(因为没有失败)
这里的大多数答案似乎都符合“因为这是如何定义的”。 但是为什么这样定义也是有逻辑的原因的。
当定义一个函数时,你希望你的函数尽可能通用,这样它可以应用于尽可能多的情况。 比如说,我想定义Sum
函数,它返回列表中所有数字的和。 列表为空时应该返回什么? 如果您返回任意数字x
,您可以将函数定义为:
- 返回给定列表中所有数字之和的函数,如果列表为空,则返回
x
。
但是如果x
是零,你也可以定义它为
- 函数返回
x
加上给定的数字。
请注意,定义2意味着定义1,但是当x
不是0时,1并不意味着2,这本身就是足够的理由来select2而不是1。但是注2也更加优雅,而且本身就比1更普遍就像把聚光灯放在更远的地方,这样可以照亮更大的区域。 实际上很大。 我本人不是math家,但我相信他们会发现定义2和其他math概念之间有很多联系,但是当x
不是零时,就没有那么多与定义1有关。
一般情况下,只要有一个函数将二元运算符应用于一组元素,并且该集合为空,就可以并最可能返回标识元素 (即保持另一个操作数不变)。 当列表为空时, Product
function将返回1的原因是相同的(请注意,您可以在定义2中将“ x
plus”replace为“one times”)。 并且是相同的原因All
(可以被认为是逻辑AND运算符的重复应用)将在列表为空( p && true
等同于p
)时返回true
,并且相同的原因Any
(OR运算符)将返回false
。
把执行放在一边。 这真的很重要吗? 看看你是否有一些迭代遍历枚举的代码并执行一些代码。 如果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.
这是一个扩展,可以做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; }
…正如其他人已经指出,这不是一个错误,而是logging良好的预期行为。
如果不想写一个新的扩展,另一个解决scheme是:
strs.DefaultIfEmpty().All(str => str == "ABC");
PS:以上不起作用,如果寻找默认值本身! (对于string将是空的。)在这种情况下,它变得不那么优雅,类似于:
strs.DefaultIfEmpty(string.Empty).All(str => str == null);
(我总体上推荐扩展方法,但是对于这种情况尤其如此。)
这很有趣,因为当我有案件
!list.Any(x=>x.Deleted==0)
ReSharperbuild议我改变它
list.All(x=>x.Deleted!=0)
这将是我的程序的一个错误:)