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
В соответствии с этой ссылкой
Любая - проверяет хотя бы одно соответствие
Все - проверяет, что все соответствуют