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

时间:2011-10-25 05:00:18

标签: c# .net linq enumerable

var strs = new Collection<string>();
bool b = strs.All(str => str == "ABC");

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

但该集合中甚至没有任何元素,更不用说任何等于“ABC”的元素了。

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

7 个答案:

答案 0 :(得分:144)

这当然不是一个错误。它的行为正好as documented

  

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

现在你可以争论它是否以这种方式工作(对我来说似乎很好;序列的每个元素都符合谓词)但首先检查它 之后,在你询问是否有什么问题之前,就是文档。 (只要方法的行为与您的预期不同,这是首先检查的。)

答案 1 :(得分:8)

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

的标识

如果你认为“所有”是“序列中没有不符合的元素”,那么这可能更有意义。

答案 2 :(得分:7)

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

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

同样适用于Any(与All相反)为空集返回false

修改

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

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

答案 3 :(得分:3)

该方法循环遍历所有元素,直到找到不满足条件的元素,或者找不到任何失败的元素。如果没有失败,则返回true。

因此,如果没有元素,则返回true(因为没有元素失败)

答案 4 :(得分:3)

这里的大多数答案似乎都是“因为这是如何定义的”。但也有一个合乎逻辑的原因,就是这样定义的原因。

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

  1. 返回给定列表中所有数字之和的函数,如果列表为空,则返回x
  2. 但如果x为零,您也可以将其定义为

    1. 返回x加上给定数字的函数。
    2. 注意,定义2意味着定义1,但是当x不为零时,1并不意味着2,这本身就足以说明选择2超过1.但是注意2是更优雅< / em>并且,就其本身而言,比1更为通用。就像将聚光灯放得更远,以便照亮更大的区域。实际上要大得多。我自己不是数学家,但我确信他们会在定义2和其他数学概念之间找到很多联系,但是当x不为零时,与定义1没有太多联系。

      一般情况下,只要你有一个函数在一组元素上应用二元运算符并且该集合是,那么你可以,并且很可能想要返回identity element(保持另一个操作数保持不变的那个)。空。这与Product函数在列表为空时将返回1的原因相同(请注意,您可以在定义2中将“x plus”替换为“一次”。并且同样的原因All(可以被认为是逻辑AND运算符的重复应用)将在列表为空时返回truep && true等同于{{1} }}),同样的原因p(OR运算符)将返回Any

答案 5 :(得分:1)

将实施放在一边。这是真的重要吗?看看你是否有一些迭代可枚举的代码并执行一些代码。如果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.

答案 6 :(得分:1)

这是一个可以做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:如果查找默认值本身,上述功能无效! (对于字符串,它将为null。) 在这种情况下,它会变得不那么优雅,类似于:

strs.DefaultIfEmpty(string.Empty).All(str => str == null);

如果您可以多次枚举最简单的解决方案:

strs.All(predicate) && strs.Any();

即只需在实际上有任何元素后添加一个检查。