LINQ: не все против всех не - PullRequest
       3

LINQ: не все против всех не

254 голосов
/ 27 января 2012

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

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

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

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

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

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

Ответы [ 8 ]

324 голосов
/ 27 января 2012

Реализация 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-для-объектов. Возможно, что какой-то другой поставщик linq рассматривает одного из них гораздо лучше, чем другого, но тогда, если бы это было так, случайным образом, какой из них получил более оптимальную реализацию.

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

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

50 голосов
/ 27 января 2012

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

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
}
23 голосов
/ 27 января 2012

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

19 голосов
/ 27 января 2012

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

Одна тонкость в том, что

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

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

Подробнее об этом методе см. В документации по Enumerable.All .

6 голосов
/ 05 ноября 2015

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 голосов
/ 06 ноября 2015

Как и другие ответы хорошо освещены: речь идет не о производительности, а о ясности.

Существует широкая поддержка обоих вариантов:

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
}

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

5 голосов
/ 26 августа 2015

По этой ссылке

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

Все - проверяет все совпадения

3 голосов
/ 22 апреля 2016

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...