Таким образом, я столкнулся с ситуацией сегодня, где некоторый производственный код перестал работать точно потому что метод, выполненный точно, как зарегистрировано в MSDN. Позор мне для того, чтобы не прочитать документацию. Однако я все еще царапаю голову относительно того, почему она ведет себя этот путь, даже если бы "дизайном", так как это поведение точно противоположно, что я ожидал бы (и другой, знал бы поведения), и поэтому, кажется, нарушает принцип наименьшего количества удивления.
All()
метод позволяет Вам предоставлять предикат (такой как лямбда-выражение) для тестирования IQueryable
, возврат булева значения, которое указывает, соответствуют ли все участники набора тесту. Пока неплохо. Вот то, где это становится странным. All()
также возвраты true
если набор пуст. Это кажется полностью назад мне по следующим причинам:
Any()
метод ведет себя как ожидалось и (правильно) возвращает false, потому что он не имеет никаких участников, которые соответствуют предикату.Таким образом, мой вопрос, почему делает All()
вести себя этот путь? Какую проблему это решает? Это нарушает принцип наименьшего количества удивления?
Я отметил этот вопрос как.NET 3.5, хотя поведение также относится к.NET 4.0 также.
ОТРЕДАКТИРУЙТЕ хорошо, таким образом, я схватываю логический аспект к этому, как так превосходно размечено Jason и остальной частью Вас. По общему признанию пустой набор является чем-то вроде пограничного случая. Я предполагаю, что мой вопрос базирован в борьбе, которая, просто потому что что-то логично, не означает, что это обязательно имеет смысл, если Вы не находитесь в корректном настроении.
Если моя дорога пуста, я не могу утверждать, что все машины, припаркованные там, красные.
Рассмотрим следующие утверждения.
S1
: Моя подъездная дорожка пуста.
S2
: Все машины, припаркованные у меня на подъездной дорожке, красные.
Я утверждаю, что S1
влечет S2
. То есть утверждение S1 => S2
верно. Я сделаю это, показав, что его отрицание ложно. В этом случае отрицание S1 => S2
есть S1 ^ ~ S2
; это потому, что S1 => S2
ложно только тогда, когда S1
истинно, а S2
ложно. Что является отрицанием S2
? Это
~ S2
: На моей подъездной дорожке припаркована машина не красного цвета.
Каково истинное значение S1 ^ ~ S2
? Давайте запишем это
S1 ^ ~ S2
: Моя подъездная дорожка пуста, а на моей подъездной дорожке припаркована машина не красного цвета.
Единственный способ, которым S1 ^ ~ S2
может быть истинным, - это если оба S1
и ~ S2
истинны. Но S1
говорит, что моя подъездная дорожка пуста, а S2
говорит, что на моей подъездной дорожке стоит машина. Моя подъездная дорожка не может быть одновременно пустой и содержать машину. Таким образом, невозможно, чтобы S1
и ~ S2
оба были истинными. Следовательно, S1 ^ ~ S2
ложно, поэтому его отрицание S1 => S2
истинно.
Следовательно, если ваша подъездная дорожка пуста, вы можете утверждать, что все припаркованные там машины красные.
Итак, теперь давайте рассмотрим IEnumerable
и Predicate
. Предположим, что элементов
пусто. Мы хотим узнать значение
bool b = elements.All(x => p(x));
. Давайте рассмотрим его отрицание
bool notb = elements.Any(x => !p(x));
Чтобы notb
было истинным, должен быть хотя бы один x
в элементах
для которого верно ! p (x)
. Но элементов
пусто, поэтому невозможно найти x
, для которого ! P (x)
истинно. Следовательно, notb
не может быть истинным, поэтому оно должно быть ложным. Поскольку notb
ложно, его отрицание истинно. Следовательно, b
истинно и элементов. Все (x => p (x))
должны быть истинными, если элементов
пусто.
Вот еще один способ подумать об этом. Предикат p
истинен, если для всех x
в элементах
вы не можете найти ни одного , для которого он ложен. Но если в элементах
нет элементов, то невозможно найти любой , для которого он является ложным. Таким образом, для пустого набора элементов
, p
истинно для всех x
в элементах
А что насчет элементы.Any (x => p (x))
, когда elements
является пустым IEnumerable
и p
является Predicate
как указано выше? Мы уже знаем, что результат будет ложным, потому что мы знаем, что его отрицание истинно, но давайте все равно рассмотрим его; интуиция ценна. Для элементов. Любой (x => p (x))
, чтобы быть истинным, должен быть хотя бы один x
в элементах
, для которого p ( x)
верно. Но если нет каких-либо x
в элементах
, невозможно найти какие-либо x
, для которых p (x)
верно. Следовательно, elements.Any (x => p (x))
ложно, если elements
пусто.
Наконец, вот связанное с объяснение того, почему s.StartsWith (String.Empty)
истинно, когда s
является ненулевым экземпляром ] строка
:
"Если коллекция пуста, такой тест , в лучшем случае, не определен. Если моя подъездная дорога пусто, я не могу утверждать , что все припаркованные там машины красные »
Да, можно.
Чтобы доказать, что я неправ, покажите мне машину на пустой подъездной дорожке, которая не является красной.
Для любого, кто знаком с понятием SQL, что NULL! = NULL, это неожиданное поведение.
Это причуда SQL (и не совсем верно: NULL = NULL
и NULL <> NULL
оба не определены, и ни один из них не соответствует ни одной строке.)
Думаю, это имеет смысл. В логике дополнения FOR ALL НЕТ (ТАКОЕ СУЩЕСТВУЕТ). FOR ALL похож на All ()
. THERE EXIST похож на Any ()
.
Итак, IQueryable.All ()
эквивалентен ! IQueryable.Any ()
. Если ваш IQueryable
пуст, то оба возвращают истину на основе документа MSDN.
Это очень похоже на базовую концепцию числа ноль. Несмотря на то, что он представляет собой существование отсутствия, он все же обладает и представляет ценность. IQueryable.All () должен возвращать true, потому что он успешно вернет всех членов коллекции. Так получилось, что если коллекция пуста, функция не вернет никаких членов, но не потому, что функция не смогла вернуть какие-либо члены. Это произошло только потому, что не было членов, которые могли бы вернуться. При этом, почему IQueryable.All () должен терпеть неудачу из-за отсутствия поддержки со стороны коллекции? Он был готов, он мог ... он был способен. Для меня это звучит так, как будто коллекция не могла выполнить свою часть сделки ...
Если количество возвращаемых элементов истина
равно то же самое, что и количество всех элементов, затем вернуть true
. Вот так просто:
Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()
Связанное объяснение: Почему "abcd" .StartsWith ("") возвращает истину?
Any()
и All()
- это всего лишь реализации обычных математических операторов ∃ ("экзистенциальный кватификатор" или "существует") и ∀ ("универсальный кватификатор" или "для всех").
"Любой" означает, что существует некий элемент, для которого верен предикат. Для пустой коллекции это было бы ложью.
"Все" означает, что не существует никакого элемента, для которого предикат является ложным. Для пустой коллекции это всегда будет правдой.
Такое поведение довольно часто встречается в других областях математики или информатики.
Оператор SUM в математике возвращает 0 (нейтральный элемент +) в случаях, когда диапазоны недействительны (SUM от 0 до -1). Оператор MULTIPYL вернёт 1 (нейтральный элемент для умножения).
Теперь, если у вас есть булевы выражения, то это очень похоже: нейтральный элемент для ИЛИ равен false
(a OR false = a
), в то время как нейтральный элемент для И равен true
.
Теперь на Linq'е ЛЮБОЙ
и ВСЕ
: Они похожи на это:
ANY = a OR b OR c OR d ...
ALL = a AND b AND c AND d ...
Так что такое поведение - это то, чего "вы бы ожидали", если у вас есть математический/школьный фон.
Возвращение истинного
также логично. У вас есть два утверждения: "У вас есть машина?" и "Она красная?" Если первое утверждение является ложным
, неважно, что такое второе утверждение, результат будет истинным
по modus ponens .
Ложно означает, что запрос не вернул результатов даже без предиката. Это просто неполный способ указать, что Dested опубликовал, когда я печатал это.