С# Linq All & Any работает по-разному на пустом массиве
Рассмотрим следующий пример linq с пустым массивом:
Когда Any()
возвращает false
, поскольку числа больше нуля, как All()
возвращает true
, передавая все числа больше нуля?
var arr = new int[] { };
Console.WriteLine(arr.Any(n => n > 0)); //false
Console.WriteLine(arr.All(n => n > 0)); //true
Ответы
Ответ 1
Кажется логичным для меня.
-
All
: все числа в arr
больше нуля (это означает, что число не больше нуля) = > true
-
Any
: Есть ли число в arr
, которое больше нуля = > false
Но более важно, согласно Булевая алгебра:
arr.All(n => n > 0);
дает true
, потому что он должен быть логически противоположным
arr.Any(n => !(n > 0));
который дает false
(на самом деле это то, что говорят выше).
Ответ 2
Реализация 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 element in source) {
if (!predicate(element)) return false;
}
return true;
}
Он выполняет foreach
по коллекции. Если в коллекции нет элементов, он пропустит foreach
и вернет true.
Интересно, что реализация на 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 element in source) {
if (predicate(element)) return true;
}
return false;
}
Это ясно показывает, что они противоположности.
Ответ 3
Реализация для All
возвращает true, если в списке нет элемента:
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; // watch this
}
Это кажется довольно противоречивым, но вот как это реализовано.
Тем не менее документы достаточно понятны для возвращаемого значения All
:
true, если каждый элемент исходной последовательности проходит тест в указанный предикат, или если последовательность пуста;
Ответ 4
Немного математической перспективы:
Any
и All
являются обобщенными версиями операторов ||
и &&
, так же как Sum
и Product
(не в LINQ) являются обобщениями +
и *
.
Обобщенные операторы при работе с пустым набором возвращают нейтральный элемент операции. Для +
это 0, для *
это 1 так emptyArray.Product() == 1
, потому что 1 является нейтральным элементом операции *
(для всех a: a * 1 == a
), для ||
это false
(a || false == a
), а для &&
это true
(a || true == a
).
Благодаря этому обобщенные операторы сохраняют ассоциативность "исходной" операции, например. для суммы: intersect(A,B) == EmptySet; S = union(A,B); S.Sum() == A.Sum() + B.Sum()
, и это будет работать, даже если одно из наборов A
или B
пусто. Другими словами, математически удобно определить, что обобщенный оператор на пустом множестве возвращает нейтральный элемент.