56 votes

Pourquoi IQueryable.All () renvoie true sur une collection vide?

Alors j'ai couru dans une situation aujourd'hui où une partie de la production de code n'était pas précisément parce qu'une méthode joué exactement comme documenté dans MSDN. Honte sur moi pour ne pas la lecture de la documentation. Cependant, je suis encore à me gratter la tête, comme pour pourquoi il se comporte de cette manière, même si "by design", puisque ce comportement est exactement à l'opposé de ce que je me serais attendu (et d'autres, des comportements connus) et, par conséquent, semble violer le principe de moindre surprise.

L' All() méthode permet de fournir un prédicat (comme une expression lambda) pour tester un IQueryable, renvoyant une valeur Booléenne qui indique si tous les membres de la collection correspondent le test. So far So good. Là où ça devient bizarre. All() également les retours true si la collection est vide. Cela semble tout à l'envers pour moi, pour les raisons suivantes:

  • Si la collection est vide, un test de ce genre est, au mieux, non défini. Si mon entrée est vide, je ne peux pas affirmer que toutes les voitures garées là sont rouges. Avec ce comportement, sur un vide allée toutes les voitures garées il y a le rouge ET le bleu ET le damier - toutes ces expressions renvoient true.
  • Pour quiconque est familier avec le SQL notion que NULL != NULL, c'est un comportement inattendu.
  • L' Any() méthode se comporte comme prévu, et (correctement) renvoie faux car il n'a pas tous les membres qui correspondent le prédicat.

Donc ma question est, pourquoi est - All() se comporter de cette façon? Ce problème permet-il de résoudre? Est-ce à violer le principe de moindre surprise?

J'ai marqué cette question .NET 3.5, si le comportement s'applique également aux .NET 4.0 ainsi.

EDIT Ok, j'ai donc saisir l'aspect logique à cela, comme l'a si excellemment exposées par Jason et le reste d'entre vous. Certes, un regroupement vide est quelque chose d'un cas limite. Je suppose que ma question est enracinée dans la lutte qui, juste parce que quelque chose est logique ne signifie pas nécessairement le fait de sens si vous n'êtes pas dans le bon état d'esprit.

51voto

Jason Points 125291

Si mon entrée est vide, je ne peux pas affirmer que toutes les voitures garées là sont rouges.

Considérons les énoncés suivants.

S1: Mon entrée est vide.

S2: Toutes les voitures garées dans mon entrée rouge.

Je demande qu' S1 implique S2. Autrement dit, l'instruction S1 => S2 est vrai. Je vais le faire en montrant que c'est la négation est fausse. Dans ce cas, la négation de l' S1 => S2 est S1 ^ ~S2; c'est parce qu' S1 => S2 est faux seulement lorsqu' S1 qui est vrai et S2 est faux. Qu'est-ce que la négation de l' S2? Il est

~S2: Il existe une voiture garée dans mon allée qui n'est pas rouge.

Quelle est la valeur de vérité de l' S1 ^ ~S2? Nous allons l'écrire

S1 ^ ~S2: Mon entrée est vide et il y a une voiture garée dans mon allée qui n'est pas rouge.

La seule façon qu' S1 ^ ~S2 peut être vrai, c'est si les deux S1 et ~S2 sont remplies. Mais S1 dit que mon entrée est vide et S2 dit qu'il existe une voiture dans mon allée. Mon entrée ne peut pas être à la fois vide et contenir une voiture. Ainsi, il est impossible pour l' S1 et ~S2 à la fois d'être vrai. Par conséquent, S1 ^ ~S2 est faux donc sa négation S1 => S2 est vrai.

Par conséquent, si votre entrée est vide, vous pouvez affirmer que toutes les voitures garées là sont rouges.

Alors maintenant, considérons un IEnumerable<T> elements et Predicate<T> p. Supposons que l' elements est vide. Nous souhaitons découvrir la valeur de

bool b = elements.All(x => p(x));

Considérons sa négation

bool notb = elements.Any(x => !p(x));

Pour notb pour être vrai, il doit y avoir au moins un x en elements dont !p(x) est vrai. Mais elements est vide, donc il est impossible de trouver un x dont !p(x) est vrai. Par conséquent, notb ne peut pas être vrai, alors il doit être faux. Depuis notb est faux, sa négation est vraie. Par conséquent, b qui est vrai et elements.All(x => p(x)) doit être vrai si elements est vide.

Voici une façon de plus pour penser à cela. Le prédicat p "est vrai si, pour tous x en elements vous ne pouvez pas trouver tout de qui elle est fausse. Mais si il n'y a pas d'éléments en elements alors qu'il est impossible de trouver de tout pour qui elle est fausse. Ainsi, pour un regroupement vide elements, p est vrai pour tous les x en elements

A ce propos, elements.Any(x => p(x)) lorsque elements est vide, IEnumerable<T> et p est Predicate<T> comme ci-dessus? Nous connaissons déjà le résultat sera faux car nous savons que sa négation est vraie, mais en voici la raison, à travers elle, de toute façon; l'intuition est précieux. Pour elements.Any(x => p(x)) pour être vrai il doit y avoir au moins un x en elements dont p(x) est vrai. Mais si il ne sont pas tout x en elements il est impossible de trouver quelque x dont p(x) est vrai. Par conséquent, elements.Any(x => p(x)) est faux si elements est vide.

Enfin, voici une explication sur pourquoi est - s.StartsWith(String.Empty) est vrai, en s est une valeur non nulle instance de string:

11voto

Dested Points 1833

Si la longueur des éléments qui renvoient true est la même que la longueur de tous les éléments, retournez true. Aussi simple que cela

Driveway.Cars (a => a.Red) .Count () == Driveway.Cars.Count ()

Voir: http://stackoverflow.com/questions/145509/why-does-abcd-startswith-return-true

5voto

finnw Points 24592

"Si la collection est vide, un test comme c'est, au mieux, non défini. Si mon entrée est vide, je ne peux pas l'affirmer que toutes les voitures garées il y a du rouge".

Oui, vous pouvez.

Pour me prouver le contraire, montrez-moi une voiture vide allée qui n'est pas rouge.

Pour quiconque est familier avec le SQL notion que NULL != NULL, c'est un comportement inattendu.

C'est un caprice de SQL (et pas tout à fait vrai: NULL = NULL et NULL <> NULL sont à la fois défini, ni correspondre à toutes les lignes.)

3voto

David Points 2491

Je pense que cela a du sens. En logique, le complément de FOR ALL n'est PAS (IL EXISTE). FOR ALL est comme All (). IL EXISTE est comme Any ()

Donc IQueryable.All () est équivalent à! IQueryable.Any (). Si votre IQueryable est vide, les deux renvoient true en fonction du document MSDN.

1voto

codekaizen Points 14819

Renvoyer true est également logique. Vous avez deux déclarations: "Vous avez une voiture?" et "Est-ce rouge?" Si la première instruction est false , peu importe la deuxième instruction, le résultat est true par modus ponens .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X