Lorsque vous créez une méthode d'extension , vous pouvez, bien sûr, appelez sur null
.Mais, contrairement à un appel d'instance de méthode, l' appelant sur null ne pas avoir à jeter un NullReferenceException
-> vous devez vérifier et de le jeter manuellement.
Pour l’implémentation de la méthode d’extension Linq, Any()
Microsoft a décidé d’envoyer une ArgumentNullException
( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).
Ça m'irrite d'avoir à écrire if( myCollection != null && myCollection.Any() )
Ai-je tort, en tant que client de ce code, de penser que, par exemple, ((int[])null).Any()
devrait revenir false
?
c#
.net
linq
extension-method
cette fin
la source
la source
null |> Seq.isEmpty
renvoieSystem.ArgumentNullException: Value cannot be null
. L'attente semble être que vous ne passerez pas de valeurs indéfinies pour quelque chose qui devrait exister, c'est donc toujours une exception lorsque vous avez un null. Si c'est une question d'initialisation, je commencerais par une séquence vide au lieu d'unnull
.null
lorsque vous traitez avec des collections, mais utilisez plutôt une collection vide dans ces cas.Any
est juste cohérent.Réponses:
J'ai un sac avec cinq pommes de terre dedans. Y a-t-il des
.Any()
pommes de terre dans le sac?" Oui " , dites-vous.
<= true
Je sors toutes les pommes de terre et les mange. Y a-t-il des
.Any()
pommes de terre dans le sac?" Non " , dites-vous.
<= false
J'incinère complètement le sac dans un feu. Y at-il des
.Any()
pommes de terre dans le sac maintenant?" Il n'y a pas de sac ."
<= ArgumentNullException
la source
Tout d'abord, il semble que ce code source va lancer
ArgumentNullException
, nonNullReferenceException
.Cela dit, dans de nombreux cas, vous savez déjà que votre collection n'est pas nulle, car ce code est appelé uniquement à partir d'un code qui sait que la collection existe déjà. Vous n'aurez donc pas à y mettre très souvent la vérification de la valeur NULL. Mais si vous ne savez pas qu’il existe, il est alors logique de vérifier avant de faire appel
Any()
à celui-ci.Oui. La question qui
Any()
répond est "cette collection contient-elle des éléments?" Si cette collection n'existe pas, alors la question elle-même est insensée; il ne peut ni contenir ni ne rien contenir, car il n'existe pas.la source
null
est équivalent à un ensemble contenant l'ensemble vide (et inversement)?{ null }
et{ {} }
devrait être équivalent. Je trouve cela fascinant. Je ne peux pas comprendre du tout..Add
on ennull
quelque sorte créer une collection pour nous aussi?Null signifie informations manquantes, pas aucun élément.
Vous pourriez envisager plus généralement d'éviter NULL. Par exemple, utilisez l'un des énumérables vides intégrés pour représenter une collection sans élément au lieu de null.
Si vous renvoyez null dans certaines circonstances, vous pouvez changer cela pour renvoyer la collection vide. (Sinon, si vous trouvez que null est renvoyé par des méthodes de bibliothèque (pas la vôtre), c'est dommage, et je les envelopperai pour les normaliser.)
Voir aussi https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty
la source
null
ne veut pas dire qu’aucun élément est une question de convention raisonnable. Il suffit de penser OLESTRINGS indigènes, où il fait exerce ses signifient que.null
cela ne signifie pas "informations manquantes".null
n'a pas une seule interprétation significative. Au lieu de cela, il s'agit de savoir comment vous le traitez.null
est parfois utilisé pour "informations manquantes", mais aussi pour "aucun élément", ainsi que pour "une erreur est survenue" ou "non initialisé" ou "conflit d'informations" ou quelque chose d'arbitraire, ad hoc.null
est très fréquemment utilisé pour signifier "pas de données". Parfois, cela a du sens. D'autres fois, pas.Outre la syntaxe null-conditionnelle , il existe une autre technique permettant de résoudre ce problème: ne laissez jamais votre variable
null
.Considérons une fonction qui accepte une collection en tant que paramètre. Si pour les besoins de la fonction,
null
et vide sont équivalents, vous pouvez vous assurer qu’il ne contient jamaisnull
au début:Vous pouvez faire la même chose lorsque vous récupérez des collections à partir d'une autre méthode:
(Notez que dans les cas où vous avez le contrôle d'une fonction similaire à la collection vide
GetWords
etnull
équivalente à celle-ci, il est préférable de renvoyer la collection vide en premier lieu.)Vous pouvez maintenant effectuer toute opération de votre choix sur la collection. Ceci est particulièrement utile si vous devez effectuer de nombreuses opérations qui échoueraient lors de la collecte
null
. Dans les cas où vous obtiendrez le même résultat en effectuant une boucle ou en interrogeant un énumérable vide, cela permettra d'éliminerif
complètement les conditions.la source
Oui, tout simplement parce que vous êtes en C # et que ce comportement est bien défini et documenté.
Si vous construisiez votre propre bibliothèque ou si vous utilisiez une langue différente avec une culture d'exception différente, il serait plus raisonnable de s'attendre à une valeur fausse.
Personnellement, j’ai l’impression que return false est une approche plus sûre qui rend votre programme plus robuste, mais au moins discutable.
la source
Si les vérifications nulles répétées vous importunent, vous pouvez créer votre propre méthode d'extension 'IsNullOrEmpty ()', afin de refléter la méthode String de ce nom et regrouper les appels null-check et .Any () dans un seul appel.
Sinon, la solution, mentionnée par @ 17 sur 26 dans un commentaire sous votre question, est également plus courte que la méthode "standard" et est raisonnablement claire pour les personnes familiarisées avec la nouvelle syntaxe null-conditionnelle.
la source
?? false
au lieu de== true
. Cela me semblerait plus explicite (plus net) car il ne traite que le cas denull
, et ce n'est plus vraiment verbeux. De plus, les==
contrôles pour les booléens déclenchent généralement mon réflexe nauséeux. =)myCollection?.Any()
assez?myCollection?.Any()
retourne effectivement un booléen nullable plutôt qu'un booléen ordinaire (c'est-à-dire bool? Au lieu de bool). Il n'y a pas de conversion implicite de bool? bool, et c'est un bool nous avons besoin dans cet exemple particulier (pour tester dans le cadre de laif
condition). Par conséquent, nous devons soit explicitement comparer,true
soit utiliser l’opérateur de coalescence nulle (??).?.
.==
cela fonctionnera réellement. Je sais déjà ce qui se passe avec un booléen intuitivement quand il ne peut être quetrue
oufalse
; Je n'ai même pas à y penser. Mais ajoutonsnull
à cela, et maintenant je dois déterminer si c'est==
correct.??
au lieu de cela, élimine simplement lenull
cas, le ramenant àtrue
etfalse
que vous comprenez déjà immédiatement. En ce qui concerne mon réflexe nauséeux, lorsque je le vois pour la première fois, mon instinct immédiat est de simplement le supprimer car il est généralement inutile, ce que vous ne voulez pas voir se produire.Si vous vous interrogez sur les attentes, vous devez réfléchir aux intentions.
null
signifie quelque chose de très différent deEnumerable.Empty<T>
Comme mentionné dans la réponse d'Erik Eidt , il existe une différence de sens entre
null
une collection vide.Voyons d'abord comment ils sont censés être utilisés.
Le livre intitulé Framework Design Guidelines: Conventions, idiomes et modèles pour les bibliothèques .NET réutilisables, 2e édition, rédigé par les architectes de Microsoft, Krzysztof Cwalina et Brad Abrams, décrit la meilleure pratique suivante:
Envisagez d’appeler une méthode qui consiste à extraire des données d’une base de données: si vous recevez un tableau vide ou si
Enumerable.Empty<T>
cela signifie simplement que votre espace d’échantillon est vide, le résultat est un ensemble vide . Recevoirnull
dans ce contexte signifierait cependant un état d'erreur .Dans le même ordre d'idées que l'analogie de Dan Wilson en matière de pommes de terre , il est logique de poser des questions sur vos données, même s'il s'agit d'un ensemble vide . Mais cela a beaucoup moins de sens, s’il n’ya pas de décor .
la source
null
et empty peut entraîner la même valeur de retour pour la fonction, mais cela ne signifie pas quenull
et vide signifie exactement la même chose dans l'étendue de l'appel.Il existe de nombreuses réponses expliquant pourquoi
null
et vide sont des opinions différentes et suffisamment nombreuses pour tenter d’ expliquer à la fois pourquoi elles devraient être traitées différemment ou non. Cependant, vous demandez:C'est une attente parfaitement raisonnable . Vous avez aussi raison que quelqu'un qui préconise le comportement actuel. Je suis d'accord avec la philosophie de mise en œuvre actuelle, mais les facteurs déterminants ne sont pas - uniquement - fondés sur des considérations hors contexte.
Étant donné que
Any()
sans prédicat est essentiellementCount() > 0
alors qu'attendez-vous de cet extrait?Ou un générique:
Je suppose que vous vous attendez
NullReferenceException
.Any()
est une méthode d’extension, elle doit s’intégrer harmonieusement à l’ objet étendu puis le lancement d’une exception est la moins surprenante.Enumerable.Any(null)
et là vous vous attendez certainementArgumentNullException
. C'est la même méthode et elle doit être compatible avec - peut-être - presque TOUT le reste du Cadre.null
objet est une erreur de programmation , le framework ne devant pas appliquer null comme valeur magique . Si vous l'utilisez de cette façon, il vous incombe de vous en occuper.Count()
semble une décision facile, ilWhere()
ne l'est pas. Qu'en est- ilMax()
? Il jette une exception pour une liste VIDE, ne devrait-il pas aussi en lancernull
une?Ce que les concepteurs de bibliothèque ont fait avant LINQ était d'introduire des méthodes explicites lorsqu'une valeur
null
est valide (par exempleString.IsNullOrEmpty()
), ils doivent ensuite être cohérents avec la philosophie de conception existante . Cela dit, même si assez trivial à écrire, deux méthodesEmptyIfNull()
etEmptyOrNull()
peut - être à portée de main.la source
Jim est supposé laisser des pommes de terre dans chaque sac. Sinon je vais le tuer.
Jim a un sac contenant cinq pommes de terre. Y a-t-il des
.Any()
pommes de terre dans le sac?"Oui", dites-vous. <= vrai
Ok, Jim vit cette fois.
Jim prend toutes les pommes de terre et les mange. Existe-t-il des pommes de terre dans le sac?
"Non", dites-vous. <= faux
Il est temps de tuer Jim.
Jim incinère complètement le sac dans un feu. Existe-t-il des pommes de terre dans le sac?
"Il n'y a pas de sac." <= ArgumentNullException
Jim doit-il vivre ou mourir? Nous ne nous attendions pas à cela, alors j’ai besoin d’une décision. Est-ce que laisser Jim s'en tirer avec ça est un bug ou pas?
Vous pouvez utiliser des annotations pour signaler que vous n'acceptez pas de mannequins nuls de cette façon.
Mais votre chaîne d’outils doit le supporter. Ce qui signifie que vous finirez probablement par écrire des chèques.
la source
Si cela vous dérange tellement, je suggère une méthode d'extension simple.
Maintenant vous pouvez faire ceci:
... et le drapeau sera réglé sur
false
.la source
return
déclaration en tant que:return source ?? Enumerable.Empty<T>();
Il s’agit d’une question sur les méthodes d’extension de C # et leur philosophie de conception. Je pense donc que le meilleur moyen de répondre à cette question est de citer la documentation de MSDN sur les méthodes d’extension :
Pour résumer, les méthodes d'extension sont conçues pour ajouter des méthodes d'instance à un type particulier, même lorsque les développeurs ne peuvent pas le faire directement. Et parce que les méthodes d'instance vont remplacent toujours les méthodes d'extension si elles sont présentes (si elles sont appelées à l'aide de la syntaxe de méthode d'instance), vous ne devez le faire que si vous ne pouvez pas ajouter directement de méthode ou étendre la classe. *
En d'autres termes, une méthode d'extension doit agir comme une méthode d'instance, car elle peut être transformée en méthode d'instance par un client. Et parce qu'une méthode d'instance doit lancer si l'objet sur lequel il est appelé est
null
, la méthode d'extension devrait l'être également.* Comme une note de côté, c'est exactement la situation que les concepteurs de LINQ face: quand C # 3.0 est sorti, il y avait déjà des millions de clients qui utilisaient
System.Collections.IEnumerable
etSystem.Collections.Generic.IEnumerable<T>
, à la fois dans leurs collections et enforeach
boucles. Ces classes sont revenus desIEnumerator
objets qui n'avaient les deux méthodesCurrent
etMoveNext
, afin d' ajouter des méthodes d'instance supplémentaires nécessaires, commeCount
,Any
, etc., enfreindrait ces millions de clients. Ainsi, afin de fournir cette fonctionnalité ( en particulier car il peut être mis en œuvre en termes deCurrent
etMoveNext
avec une relative facilité), ils sortent comme des méthodes d'extension, qui peuvent être appliquées à tout moment existanteIEnumerable
exemple et peut également être implémenté par les classes de manière plus efficace. Si les concepteurs de C # avaient décidé de publier LINQ dès le premier jour, cela aurait été fourni comme méthode d'instanceIEnumerable
et ils auraient probablement conçu un système pour fournir des implémentations d'interface par défaut de ces méthodes.la source
Je pense que l’essentiel ici est qu’en renvoyant la valeur false au lieu de lever une exception, vous masquerez les informations pouvant présenter un intérêt pour les futurs lecteurs / modificateurs de votre code.
S'il est possible que la liste soit nulle, je suggérerais d'avoir un chemin logique séparé pour cela, car sinon, à l'avenir, quelqu'un pourrait ajouter une logique basée sur une liste (comme un ajout) à else {} de votre if, entraînant ainsi une exception non désirée qu’ils n’ont aucune raison de prédire.
La lisibilité et la maintenabilité sont des atouts majeurs: «Je dois écrire une condition supplémentaire» à chaque fois.
la source
En règle générale, j'écris l'essentiel de mon code pour supposer que l'appelant est responsable de ne pas me transmettre de données nulles, principalement pour les raisons décrites dans Dites non à nul (et autres publications similaires). Le concept de null est souvent mal compris et, pour cette raison, il est conseillé d’initialiser vos variables à l’avance, autant que possible. En supposant que vous travailliez avec une API raisonnable, vous devriez idéalement ne jamais récupérer une valeur null. Vous ne devriez donc jamais avoir à vérifier la valeur null. Le point null, comme d'autres réponses l'ont indiqué, est de vous assurer que vous disposez d'un objet valide (par exemple, un "sac") avec lequel travailler avant de continuer. Ce n'est pas une fonctionnalité de
Any
, mais plutôt une fonctionnalité de la languelui-même. Vous ne pouvez pas effectuer la plupart des opérations sur un objet null, car il n'existe pas. C'est comme si je vous demandais de conduire au magasin avec ma voiture, sauf que je ne possède pas de voiture, vous n'avez donc aucun moyen d'utiliser ma voiture pour aller au magasin. Il est absurde de faire une opération sur quelque chose qui n'existe pas littéralement.Il existe des cas pratiques d'utilisation de null, par exemple lorsque vous ne disposez littéralement pas des informations demandées (par exemple, si je vous ai demandé quel est mon âge, le choix le plus probable pour vous de répondre à ce stade est "Je ne sais pas ", qui est ce qu’une valeur nulle est). Cependant, dans la plupart des cas, vous devriez au moins savoir si vos variables sont initialisées ou non. Et si vous ne le faites pas, alors je vous recommanderais de resserrer votre code. La première chose que je fais dans un programme ou une fonction est d’initialiser une valeur avant de l’utiliser. Il est rare que je doive vérifier les valeurs NULL, car je peux garantir que mes variables ne sont pas nulles. C'est une bonne habitude à prendre, et plus vous vous rappelez d'initialiser vos variables fréquemment, moins vous devez vérifier la valeur null.
la source