LINQ:不是所有人都不要

时间:2012-01-27 00:31:00

标签: c# .net performance linq resharper

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

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

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

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

显然,这在逻辑上是相同的,可能稍微更具可读性(如果你已经做了很多数学),我的问题是:这是否会导致性能下降?

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

8 个答案:

答案 0 :(得分:321)

根据ILSpy实现All(正如我实际去看看,而不是“好吧,那种方法有点像......”如果我们讨论的是理论而不是影响)。

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;
}

根据ILSpy实施Any

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-for-objects。有可能其他一些linq提供者比另一个更好地对待一个,但如果是这样的话,那么它就是随机获得更优化的实现。

似乎该规则仅归功于某人感觉if(determineSomethingTrue)if(!determineSomethingFalse)更简单,更易读。而且公平地说,我认为他们有点关注我经常发现if(!someTest)令人困惑*当有一个相同的冗长和复杂性的替代测试时,对于我们想要采取行动的条件会返回真实。但实际上,我个人并没有发现任何东西比你给出的两种替代方案更有利,如果谓词更复杂,也许会略微倾向于前者。

*并不像我不理解那样令人困惑,但令人困惑,因为我担心我不理解的决定有一些微妙的原因,并且需要一些精神上的跳跃才能意识到“不,他们只是决定这样做,等待我再看一下这段代码?......“

答案 1 :(得分:49)

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

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
}

答案 2 :(得分:23)

两者都具有相同的性能,因为在结果可以确定后停止枚举 - Any()对传递的谓词求值为true的第一项和All()对谓词的第一项进行判断评估为false

答案 3 :(得分:19)

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

一个微妙的领域是

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

是真的。序列中的所有项目均匀。

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

答案 4 :(得分:6)

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

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

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

答案 5 :(得分:5)

根据此link

  

任何 - 检查至少一场比赛

     

全部 - 检查所有匹配

答案 6 :(得分:5)

正如其他答案所涵盖的那样:这不是关于表现,而是关于清晰度。

对您的两种选择都有广泛的支持:

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
}

在否定任何事情之前简单地计算布尔值(并命名它)在我的脑海中清除了很多。

答案 7 :(得分:3)

如果您查看Enumerable source,我会发现AnyAll的实施非常接近:

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;
}

一种方法没有办法明显快于另一种方法,因为唯一的区别在于布尔否定,所以更喜欢可读性而非虚假性能胜利。