LINQ:不是任何人都不是所有人


272

我经常想检查一个提供的值是否与列表中的一个匹配(例如,在验证时):

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

最近,我注意到ReSharper要求我将这些查询简化为:

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

显然,这在逻辑上是相同的,也许可读性更高(如果您完成了许多数学运算),我的问题是:这会导致性能下降吗?

感觉像是应该的(即.Any()听起来像是短路,而.All()听起来像不是),但是我没有任何证据可以证明这一点。是否有人对查询是否可以解决相同的问题或ReSharper是否使我误入歧途有更深入的了解?


6
您是否尝试过分解Linq代码以查看其作用?
RQDQ 2012年

9
在这种情况下,我实际上会使用if(!acceptedValues.Contains(someValue)),但当然这不是问题:)
csgero 2012

2
@csgero,我同意。上面是真实逻辑的简化(也许过分简化)。
2012年

1
“感觉应该这样(即.Any()听起来像是短路,而.All()听起来像不是短路)”-不适合任何有直觉的人。您注意到的逻辑上的对等意味着它们同样可以短路。片刻的想法表明,一旦遇到不合格的案件,所有人都可以退出。
Jim Balter 2014年

3
我对此并不普遍同意ReSharper。写出明智的思路。如果您缺少必填项,则要抛出异常:if (!sequence.Any(v => v == true))。如果您希望仅在一切都符合特定规范的情况下继续进行操作:if (sequence.All(v => v < 10))
Timo

Answers:


344

All根据ILSpy的实现(就像我实际去看的那样,而不是“嗯,这种方法的工作原理类似于……”,如果我们在讨论理论而不是影响,我可能会这样做)。

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

实施Any根据ILSpy:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return true;
        }
    }
    return false;
}

当然,产生的IL可能会有一些细微的差异。但是不,不,没有。IL几乎相同,但是明显的颠倒是谓词匹配返回true,而谓词不匹配返回false。

当然,这仅是针对对象的。某些其他linq提供程序可能会比另一种更好地对待它,但是如果是这种情况,哪个获得了最佳实现是非常随机的。

似乎规则完全归因于某人感觉if(determineSomethingTrue)if(!determineSomethingFalse)。公平地说,我认为它们有一点意义,即if(!someTest)当对相同的冗长性和复杂性进行另类测试时,对于我们要采取的条件返回真实值时,我常常会感到困惑*。但是,实际上,我个人没有发现哪一种可以比您提供的两种选择中的另一种更好,并且如果谓词更复杂,则可能会略微倾向于前者。

*不要因为我不明白而感到困惑,但是因为担心我不明白的决定有一些微妙的原因而使我感到困惑,并且需要花些时间跳过才能意识到“不,他们只是决定做这样,等我又在看这段代码吗?...”


8
我不确定该怎么做,但是对我来说更具可读性的是:if(不是任何)比if(所有都不相等)。
VikciaR 2012年

49
当枚举没有值时,会有很大的不同。“任何”将始终返回FALSE,而“全部”将始终返回TRUE。因此,说一个在逻辑上等同于另一个是不完全正确的!
Arnaud 2013年

44
@Arnaud Any将返回false,因此!Any将返回true,因此它们是相同的。
乔恩·汉纳

11
@Arnaud没有人评论说Any和All在逻辑上是等效的。换句话说,所有发表评论的人都没有说任何和全部在逻辑上是等效的。等价在!Any(谓词)和All(!predicate)之间。
吉姆·巴尔特

7
@MacsDickinson完全没有区别,因为您没有比较相反的谓词。相当于!test.Any(x => x.Key == 3 && x.Value == 1)使用的Alltest.All(x => !(x.Key == 3 && x.Value == 1))(这的确是相当于test.All(x => x.Key != 3 || x.Value != 1))。
乔恩·汉纳

55

您可能会发现以下扩展方法使您的代码更具可读性:

public static bool None<TSource>(this IEnumerable<TSource> source)
{
    return !source.Any();
}

public static bool None<TSource>(this IEnumerable<TSource> source, 
                                 Func<TSource, bool> predicate)
{
    return !source.Any(predicate);
}

现在代替原来的

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

你可以说

if (acceptedValues.None(v => v == someValue))
{
    // exception logic
}

6
谢谢-我已经在考虑在我们的commons库中实现这些功能,但是我还没有决定这是否是个好主意。我同意它们使代码更具可读性,但是我担心它们不能增加足够的价值。
2012年

2
我寻找“无”,但没有找到。它更具可读性。
Rhyous

我必须添加空检查:return source == null || !source.Any(谓词);
Rhyous

27

双方将具有相同的性能,因为结果后都停止计数可确定- Any()第一个项目所传递的谓词为trueAll()第一项的断言求false


21

All 第一次不匹配时发生短路,因此这不是问题。

一个微妙的领域是

 bool allEven = Enumerable.Empty<int>().All(i => i % 2 == 0); 

是真的。序列中的所有项目均为偶数。

有关此方法的更多信息,请参阅Enumerable.All的文档。


12
是的,但bool allEven = !Enumerable.Empty<int>().Any(i => i % 2 != 0)也是如此。
乔恩·汉娜

1
@Jon在语义上没有!=全部。因此,从语义上讲,您要么没有任何一个,要么只有一个,但对于.All()来说,没有一个只是所有返回true的所有集合的子集,如果您不知道,差异可能会导致错误。安东尼+1
符文FS

@RuneFS我不关注。从语义和逻辑上讲,“没有一个……不真实的地方”的确与“所有在那个地方真实的地方”都是一样的。例如:“我们公司没有接受任何项目?” 的答案将始终与“其他公司接受的所有项目都在哪里?” ...
Jon Hanna 2012年

...现在,确实可以假设“所有项目都是...”而产生错误,这意味着至少有一个项目至少是一项符合测试要求的项目,因为“所有项目... ”对于空集始终是正确的,我一点也不怀疑。我补充说,假设“没有一个项目...”可能会发生相同的问题,这意味着至少一个项目没有通过测试,因为“空项目...”对于空集也总是成立。我并不是不同意安东尼的观点,而是我认为这也适用于所讨论的两个构架中的另一个。
乔恩·汉娜

@Jon,您在谈论逻辑,我在谈论语言学。人脑无法处理负值(在处理正值之前,它可以将其否定),因此从这个意义上讲,两者之间存在很大差异。这不会使您提出的逻辑不正确
符文FS

8

All()确定序列中的所有元素是否都满足条件。
Any()确定序列中的任何元素是否满足条件。

var numbers = new[]{1,2,3};

numbers.All(n => n % 2 == 0); // returns false
numbers.Any(n => n % 2 == 0); // returns true

7

根据这个链接

任意–检查至少一场比赛

全部–检查所有匹配项


1
您是对的,但对于给定的收藏夹它们同时停止。当条件失败时,所有中断;当与谓词匹配时,所有中断。因此,从技术上讲,除了风景如画
WPFKK'Aug

6

正如其他答案已经很好地涵盖了:这与性能无关,而与清晰度有关。

这两种选择均得到广泛支持:

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

但是我认为这可能会获得更广泛的支持

var isValueAccepted = acceptedValues.Any(v => v == someValue);
if (!isValueAccepted)
{
    // exception logic
}

在否定任何东西之前简单地计算布尔值(并将其命名)就可以在我的脑海中解决这个问题。


3

如果你在看看可枚举源你会看到的实施Any,并All非常接近:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (!predicate(element)) return false;
    }
    return true;
}

一种方法不可能比另一种方法快得多,因为唯一的区别在于布尔取反,因此更喜欢可读性而不是错误的性能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.