LINQ: не любой vs All Do not

Часто я хочу проверить, соответствует ли предоставленное значение одному из списка (например, при проверке):

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

Недавно я заметил, что ReSharper попросил меня упростить эти запросы:

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

Очевидно, что это логически идентично, возможно, немного читаемо (если вы сделали много математики), мой вопрос: приводит ли это к результату?

Похоже, что (например, .Any() звучит как короткое замыкание, тогда как .All() звучит так, как будто это не так), но мне нечего доказывать это. Кто-нибудь имеет более глубокие знания относительно того, будут ли запросы разрешаться одинаково, или же ReSharper лишает меня возможности заблудиться?

Ответ 1

Реализация 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 почти то же самое, но для очевидной инверсии возврата true на соответствие предикату против возврата false на несоответствие предикатов.

Это, конечно, linq-for-objects. Возможно, что какой-то другой провайдер linq рассматривает один намного лучше, чем другой, но если это так, то он довольно случайный, который получает более оптимальную реализацию.

Казалось бы, правило сводится только к кому-то, чувствующему, что if(determineSomethingTrue) проще и читабельнее, чем if(!determineSomethingFalse). И, честно говоря, я думаю, что у них немного точка в том, что я часто нахожу if(!someTest) confusing *, когда есть альтернативный тест с равными многословиями и сложностью, который вернет true для условия, над которым мы хотим действовать. Но на самом деле я лично не нашел ничего, чтобы поддержать одну из двух альтернатив, которые вы даете, и, возможно, немного наклонился бы к первому, если бы предикат был более сложным.

* Не смущаясь, как я не понимаю, но сбиваю с толку, как в том, что я беспокоюсь, что есть какая-то тонкая причина для решения, которое я не понимаю, и требуется несколько умственных пропусков, чтобы понять, что "нет, они просто решил сделать это так, подождите, что я снова смотрю на этот бит кода?..."

Ответ 2

Вы можете обнаружить, что эти методы расширения делают ваш код более читаемым:

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
}

Ответ 3

Оба будут иметь одинаковую производительность, потому что обе стоп-перечисление после определения результата можно определить - Any() для первого элемента, который переданный предикат оценивает до true и All() в первом элементе, который предикат оценивает до false.

Ответ 4

All коротких замыканий в первом несоответствии, поэтому это не проблема.

Одной областью тонкости является то, что

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

Это правда. Все элементы в последовательности четные.

Подробнее об этом методе см. в документации для Enumerable.All.

Ответ 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
}

Просто вычисляя логическое (и называя его) перед тем, как отрицать что-либо, все это сильно улаживает меня.

Ответ 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

Ответ 7

Если вы посмотрите на Перечислимый источник, вы увидите, что реализация 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;
}

Нет никакого способа, чтобы один метод был значительно быстрее, чем другой, поскольку единственное различие заключается в логическом отрицании, поэтому предпочитайте читаемость над ложным выигрышем производительности.

Ответ 8

В соответствии с этой ссылкой

Любая - проверяет хотя бы одно соответствие

Все - проверяет, что все соответствуют