Not Any vs All Don't

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

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

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

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

显然,这在逻辑上是相同的,也许可读性稍强(如果您做了大量的数学运算),我的问题是:这会导致性能下降吗?

感觉它应该(即.Any()听起来像它短路,而.All()听起来像它没有),但我没有任何证据来证实这一点。有没有人有更深层次的知识,是否查询将解决相同的,或者ReSharper是否引导我误入歧途?

102666 次浏览

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

一个微妙的地方是

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

是真的。序列中的所有项都是偶数。

有关此方法的更多信息,请参阅可列举的。所有的文档。

两者将具有相同的性能,因为两者都在可以确定结果后停止枚举-传递的谓词的第一个项上的Any()计算为true,而谓词的第一个项上的All()计算为false

根据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几乎是相同的,但是对于在谓词匹配时返回真值和在谓词不匹配时返回假值的明显反转而言。

当然,这只是对象的linq。有可能其他的linq提供程序对其中一个的处理要比另一个好得多,但如果是这种情况,那么哪一个得到了更优的实现几乎是随机的。

这条规则似乎只归结于某些人觉得if(determineSomethingTrue)if(!determineSomethingFalse)更简单,更可读。公平地说,我认为他们有一点道理,因为当有另一种同样冗长和复杂的测试时,我经常发现if(!someTest)令人困惑*,对于我们想要采取行动的条件,它会返回true。不过,说真的,我个人觉得在你给出的两个选择中,没有哪一个比另一个更有利,如果谓词再复杂一些,我可能会稍微倾向于前者。

*困惑不是指我不理解,而是指我担心我不理解的决定中存在一些微妙的原因,并且需要在头脑中跳过几次才能意识到“不,他们只是决定这样做,等等,我为什么要看这段代码?……”

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

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
}

根据这个链接

Any -检查至少一个匹配

All -检查所有匹配

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

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


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

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

你的两个选择都得到了广泛的支持:

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
}

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

如果你看一下可列举的来源,你会发现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;
}

一种方法不可能明显比另一种方法快,因为唯一的区别在于布尔否定,所以更喜欢可读性而不是虚假性能。