Подтвердить что ты не робот

Почему Enumerable.All возвращает true для пустой последовательности?

var strs = new Collection<string>();
bool b = strs.All(str => str == "ABC");

Код создает пустой набор строк, затем пытается определить, являются ли все элементы в коллекции "ABC". Если вы запустите его, будет b.

Но в коллекции нет даже каких-либо элементов, не говоря уже о любых элементах, которые равны "ABC".

Является ли это ошибкой или есть разумное объяснение?

4b9b3361

Ответ 1

Это, конечно, не ошибка. Он ведет себя как как задокументировано:

true, если каждый элемент исходной последовательности проходит тест в указанном предикате, или если последовательность пуста; в противном случае - false.

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

Ответ 2

Все требует, чтобы предикат был истинным для всех элементов последовательности. Это явно указано в документации. Это также единственное, что имеет смысл, если вы думаете обо всем, как о логическом и между результатами предиката для каждого элемента. "Истина", которую вы выбрали для пустой последовательности, является элементом идентификации и операции. Точно так же ложь, которую вы получаете от Any для пустой последовательности, является идентификатором для логического или.

Если вы думаете о "все" как о "в последовательности нет элементов", это может иметь больше смысла.

Ответ 3

Это true, поскольку ничто (без условия) не делает его false.

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

То же самое происходит для Any (напротив All), возвращающего false для пустых множеств.

Edit:

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

foreach (var e in elems)
{
  if (!cond(e))
    return false;
}
return true; // no escape from loop

Ответ 4

Метод циклически проходит через все элементы до тех пор, пока не найдет тот, который не удовлетворяет условию, или не находит ни одного из них. Если никто не сбой, возвращается значение true.

Итак, если нет элементов, возвращается true (поскольку не было ничего, что не удалось)

Ответ 5

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

При определении функции вы хотите, чтобы ваша функция была как можно более общей, чтобы ее можно было применять к максимально возможному числу случаев. Скажем, например, что я хочу определить функцию Sum, которая возвращает сумму всех чисел в списке. Что он должен вернуть, когда список пуст? Если вы вернете произвольное число x, вы должны определить функцию как:

  • Функция, которая возвращает сумму всех чисел в указанном списке или x, если список пуст.

Но если x равно нулю, вы также можете определить его как

  1. Функция, которая возвращает x плюс заданные числа.

Заметим, что определение 2 подразумевает определение 1, но 1 не означает 2, когда x не равно нулю, что само по себе является достаточным основанием для выбора 2 над 1. Но также примечание 2 более изящно и само по себе, более общий, чем 1. Это похоже на то, чтобы прожектор находился дальше, чтобы он осветил большую площадь. На самом деле намного больше. Я сам не математик, но я уверен, что они найдут тонну связей между определением 2 и другими математическими понятиями, но не так много, связанных с определением 1, когда x не равно нулю.

В общем, вы можете и, скорее всего, захотите вернуть элемент идентификации (тот, который оставил другой операнд без изменений) всякий раз, когда вы функция, которая применяет двоичный оператор по множеству элементов, а множество пусто. По той же причине функция Product вернет 1, когда список пуст (обратите внимание, что вы можете просто заменить "x plus" на "один раз" в определении 2). И та же самая причина All (которая может рассматриваться как повторное применение логического оператора И), вернет true, когда список пуст (p && true эквивалентен p), и по той же причине Any (оператор OR) вернет false.

Ответ 6

Сохранение реализации. Действительно ли это имеет значение, если это правда? Посмотрите, есть ли у вас код, который выполняет итерацию по перечислимому и выполняет некоторый код. если All() истинно, тогда этот код все равно не будет запускаться, поскольку перечисляемый не имеет в нем никаких элементов.

var hungryDogs = Enumerable.Empty<Dog>();
bool allAreHungry = hungryDogs.All(d=>d.Hungry);    
if (allAreHungry)
    foreach (Dog dog in hungryDogs)
         dog.Feed(biscuits); <--- this line will not run anyway.

Ответ 7

Вот расширение, которое может делать то, что OP хотел сделать:

static bool All<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool mustExist)
{
    foreach (var e in source)
    {
        if (!predicate(e))
            return false;
        mustExist = false;
    }
    return !mustExist;
}

... и, как уже отмечали другие, это не ошибка, а хорошо документированное намеренное поведение.

Альтернативное решение, если вы не хотите писать новое расширение, следующее:

strs.DefaultIfEmpty().All(str => str == "ABC");

PS: Вышеуказанное не работает, если вы ищете значение по умолчанию! (Который для строк был бы нулевым.) В таких случаях он становится менее элегантным с чем-то похожим на:

strs.DefaultIfEmpty(string.Empty).All(str => str == null);

(Я рекомендую общий подход к расширению, но особенно для таких случаев.)

Ответ 8

Это смешно, потому что, когда у меня есть случай

!list.Any(x=>x.Deleted==0)

ReSharper предлагает мне изменить его на

list.All(x=>x.Deleted!=0)

Это будет ошибка для моей программы:)