LINQ:不是所有人都不要

Mar*_*ark 261 .net c# linq resharper performance

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

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

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

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

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

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

Jon*_*nna 331

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

当然,IL产生的细微差别可能会有所不同.但不,不,没有.IL几乎是相同的,但是对于在谓词匹配上返回true而在谓词不匹配时返回false的明显反转.

这当然只是linq-for-objects.有可能其他一些linq提供者比另一个更好地对待一个,但如果是这样的话,那么它是非常随机的,哪个获得了更优化的实现.

似乎规则只归结为某人感觉if(determineSomethingTrue)更简单,更可读if(!determineSomethingFalse).而且公平地说,我认为它们有点重要,因为我经常会发现if(!someTest)令人困惑的*当有一个相同的冗长和复杂性的替代测试时,我们想要采取行动的条件会恢复正常.但实际上,我个人并没有发现任何一个优于你提供的两个替代方案中的另一个,如果谓词更复杂,也许会略微倾向于前者.

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

  • 当您的枚举没有值时,有一个很大的区别.'any'总是返回FALSE,'All'总是返回TRUE.所以说一个是另一个逻辑等同于另一个并不完全正确! (46认同)
  • @Arnaud`Any`将返回`false`,因此`!Any`将返回`true`,所以它们是相同的. (39认同)
  • 我不确定在线后面做了什么,但对我来说更可读的是:if(不是任何)而不是if(all not equal). (7认同)
  • @Arnaud没有任何人评论谁说Any和All在逻辑上是等价的.或者换句话说,所有评论的人都没有说Any和All在逻辑上是等价的.等价在!Any(谓词)和All(!谓词)之间. (7认同)
  • @MacsDickinson根本没有区别,因为你没有比较相反的谓词.相当于使用`All`的`!test.Any(x => x.Key == 3 && x.Value == 1)```test.All(x =>!(x.Key == 3 &&) x.Value == 1))`(实际上相当于`test.All(x => x.Key!= 3 || x.Value!= 1)`). (7认同)
  • @AnthonyMason 不,他们都会在同一个实例上对电路进行排序。集合的大小将具有零影响。 (3认同)

Aak*_*shM 53

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

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);
}
Run Code Online (Sandbox Code Playgroud)

而不是你原来的

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

你可以说

if (acceptedValues.None(v => v == someValue))
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢 - 我已经考虑在我们的公共图书馆中实现这些,但我还没有决定它是否是一个好主意.我同意他们使代码更具可读性,但我担心他们没有增加足够的价值. (6认同)
  • 我寻找“无”,但没有找到。它更具可读性。 (2认同)

Bro*_*ass 23

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


Ant*_*ram 19

All 第一次不匹配的短路,所以这不是问题.

一个微妙的领域是

 bool allEven = Enumerable.Empty<int>().All(i => i % 2 == 0); 
Run Code Online (Sandbox Code Playgroud)

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

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

  • 是的,但是`bool allEven =!Enumerable.Empty <int>().任何(i => i%2!= 0)`也是如此. (12认同)

emy*_*emy 7

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

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

numbers.All(n => n % 2 == 0); // returns false
numbers.Any(n => n % 2 == 0); // returns true
Run Code Online (Sandbox Code Playgroud)


rca*_*ier 6

根据这个链接

任意–检查至少一场比赛

全部–检查所有匹配项

  • 你是对的,但他们同时停止给定的集合。当条件失败时全部中断,当条件匹配您的谓词时全部中断。所以技术上除了场景外没有什么不同 (3认同)

Mic*_*ren 5

正如其他答案已涵盖的一样:这与性能无关,而与清晰度有关。

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

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

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

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

var isValueAccepted = acceptedValues.Any(v => v == someValue);
if (!isValueAccepted)
{
    // exception logic
}
Run Code Online (Sandbox Code Playgroud)

在否定任何东西之前简单地计算布尔值(并命名它)就可以使我头脑清醒。


Tho*_*oub 5

如果你在看看可枚举源你会看到的实施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;
}
Run Code Online (Sandbox Code Playgroud)

一种方法不可能比另一种方法快得多,因为唯一的区别在于布尔否定,所以更喜欢可读性而不是错误的性能胜利。