Quelle est la meilleure pratique lors du renvoi de données à partir de fonctions. Est-il préférable de retourner un Null ou un objet vide? Et pourquoi l'un devrait-il faire l'un sur l'autre?
Considère ceci:
public UserEntity GetUserById(Guid userId)
{
//Imagine some code here to access database.....
//Check if data was returned and return a null if none found
if (!DataExists)
return null;
//Should I be doing this here instead?
//return new UserEntity();
else
return existingUserEntity;
}
Permet de prétendre qu'il y aurait des cas valides dans ce programme qu'il n'y aurait aucune information utilisateur dans la base de données avec ce GUID. J'imagine qu'il ne serait pas approprié de lever une exception dans ce cas ?? J'ai également l'impression que la gestion des exceptions peut nuire aux performances.
if (!DataExists)
.Réponses:
Renvoyer null est généralement la meilleure idée si vous avez l'intention d'indiquer qu'aucune donnée n'est disponible.
Un objet vide implique que des données ont été retournées, alors que le retour de null indique clairement que rien n'a été retourné.
En outre, le retour d'une valeur Null entraînera une exception Null si vous tentez d'accéder aux membres de l'objet, ce qui peut être utile pour mettre en évidence le code bogué - tenter d'accéder à un membre de rien n'a aucun sens. L'accès aux membres d'un objet vide n'échouera pas, ce qui signifie que les bogues peuvent rester inconnus.
la source
bool GetUserById(Guid userId, out UserEntity result)
- que je préférerais à la valeur de retour "nulle", et qui n'est pas aussi extrême que de lever une exception. Il permet un beaunull
code commeif(GetUserById(x,u)) { ... }
.Cela dépend de ce qui a le plus de sens pour votre cas.
Est-il sensé de retourner null, par exemple "aucun utilisateur n'existe"?
Ou est-il judicieux de créer un utilisateur par défaut? Cela est plus logique lorsque vous pouvez supposer en toute sécurité que si un utilisateur N'EXISTE PAS, le code appelant a l'intention qu'un tel existe lorsqu'il le demande.
Ou est-il judicieux de lever une exception (à la "FileNotFound") si le code appelant exige un utilisateur avec un ID non valide?
Cependant - du point de vue de la séparation des préoccupations et du PÉR, les deux premiers sont plus corrects. Et techniquement, le premier est le plus correct (mais seulement par un cheveu) - GetUserById ne devrait être responsable que d'une chose - obtenir l'utilisateur. Gérer son propre cas "utilisateur n'existe pas" en renvoyant quelque chose d'autre pourrait être une violation de SRP. Séparer en un chèque différent -
bool DoesUserExist(id)
serait approprié si vous choisissez de lever une exception.Sur la base de nombreux commentaires ci - dessous : s'il s'agit d'une question de conception au niveau de l'API, cette méthode pourrait être analogue à "OpenFile" ou "ReadEntireFile". Nous «ouvrons» un utilisateur à partir d'un référentiel et hydratons l'objet à partir des données résultantes. Une exception pourrait être appropriée dans ce cas. Peut-être pas, mais ça pourrait l'être.
Toutes les approches sont acceptables - cela dépend simplement, en fonction du contexte plus large de l'API / de l'application.
la source
Personnellement, j'utilise NULL. Il indique clairement qu'il n'y a aucune donnée à renvoyer. Mais il y a des cas où un objet nul peut être utile.
la source
Si votre type de retour est un tableau, retournez un tableau vide sinon retournez null.
la source
null
. Il vous permet de l'utiliserforeach
sans souci pour les instructions et les requêtes linqNullReferenceException
.Vous devez lever une exception (uniquement) si un contrat spécifique est rompu.
Dans votre exemple spécifique, demander une UserEntity basée sur un ID connu, cela dépendra du fait si des utilisateurs manquants (supprimés) sont un cas attendu. Si c'est le cas, retournez-le,
null
mais si ce n'est pas un cas attendu, lancez une exception.Notez que si la fonction était appelée,
UserEntity GetUserByName(string name)
elle ne lancerait probablement pas mais retournerait null. Dans les deux cas, renvoyer une UserEntity vide serait inutile.Pour les chaînes, les tableaux et les collections, la situation est généralement différente. Je me souviens d'une forme de ligne directrice MS que les méthodes devraient accepter
null
comme une liste «vide» mais renvoyer des collections de longueur nulle plutôt quenull
. De même pour les cordes. Notez que vous pouvez déclarer des tableaux vides:int[] arr = new int[0];
la source
.Wher(p => p.Lastname == "qwerty")
devrait retourner une collection vide, nonnull
.Il s'agit d'une question commerciale, selon que l'existence d'un utilisateur avec un identifiant de guidage spécifique est un cas d'utilisation normal attendu pour cette fonction, ou est-ce une anomalie qui empêchera l'application de remplir avec succès la fonction que cette méthode fournit à l'utilisateur s'opposer à ...
S'il s'agit d'une "exception", dans la mesure où l'absence d'un utilisateur avec cet ID empêchera l'application de remplir avec succès la fonction qu'elle exécute (supposons que nous créons une facture pour un client auquel nous avons expédié le produit ... ), cette situation doit alors lever une ArgumentException (ou une autre exception personnalisée).
Si un utilisateur manquant est ok, (l'un des résultats normaux potentiels de l'appel de cette fonction), retournez un null
EDIT: (pour répondre au commentaire d'Adam dans une autre réponse)
Si l'application contient plusieurs processus métier, dont un ou plusieurs nécessitent un utilisateur pour se terminer avec succès, et dont un ou plusieurs peuvent se terminer sans utilisateur, alors l'exception doit être levée plus haut dans la pile des appels, plus près de l'endroit où les processus métier qui nécessitent un utilisateur appellent ce fil d'exécution. Les méthodes entre cette méthode et ce point (où l'exception est levée) devraient simplement communiquer qu'aucun utilisateur n'existe (null, booléen, peu importe - c'est un détail d'implémentation).
Mais si tous les processus de l'application nécessitent un utilisateur, je lèverais quand même l'exception dans cette méthode ...
la source
Personnellement, je retournerais null, car c'est ainsi que je m'attendrais à ce que la couche DAL / Repository agisse.
S'il n'existe pas, ne renvoyez rien qui pourrait être interprété comme ayant réussi à récupérer un objet,
null
fonctionne à merveille ici.La chose la plus importante est d'être cohérent dans votre couche DAL / Repos, de cette façon vous ne vous trompez pas sur la façon de l'utiliser.
la source
J'ai tendance à
return null
si l'ID d'objet n'existe pas alors qu'on ne sait pas au préalable s'il doit exister.throw
si l'ID d'objet n'existe pas alors qu'il devrait exister.Je différencie ces deux scénarios avec ces trois types de méthodes. Première:
Seconde:
Troisième:
la source
out
pasref
Une autre approche consiste à passer un objet de rappel ou un délégué qui opérera sur la valeur. Si aucune valeur n'est trouvée, le rappel n'est pas appelé.
Cela fonctionne bien lorsque vous voulez éviter les vérifications nulles partout dans votre code, et lorsque vous ne trouvez pas de valeur, ce n'est pas une erreur. Vous pouvez également fournir un rappel lorsqu'aucun objet n'est trouvé si vous avez besoin d'un traitement spécial.
La même approche utilisant un seul objet pourrait ressembler à ceci:
Du point de vue de la conception, j'aime vraiment cette approche, mais elle a l'inconvénient de rendre le site d'appel plus volumineux dans les langues qui ne prennent pas facilement en charge les fonctions de première classe.
la source
Nous utilisons CSLA.NET et il considère qu'une extraction de données ayant échoué doit renvoyer un objet "vide". C'est en fait assez ennuyeux, car il exige la convention de vérifier si
obj.IsNew
plutôt queobj == null
.Comme mentionné dans une affiche précédente, les valeurs de retour nulles entraîneront immédiatement l'échec du code, réduisant ainsi la probabilité de problèmes de furtivité causés par des objets vides.
Personnellement, je pense que
null
c'est plus élégant.C'est un cas très courant, et je suis surpris que les gens ici semblent surpris: sur n'importe quelle application Web, les données sont souvent récupérées à l'aide d'un paramètre de chaîne de requête, qui peut évidemment être modifié, ce qui oblige le développeur à gérer les incidences de "non trouvé". ".
Vous pouvez gérer cela en:
... mais c'est un appel supplémentaire à la base de données à chaque fois, ce qui peut être un problème sur les pages à fort trafic. Tandis que:
... ne nécessite qu'un seul appel.
la source
Je préfère
null
, car il est compatible avec l'opérateur de coalescence nulle (??
).la source
Je dirais retourner null au lieu d'un objet vide.
Mais l'instance spécifique que vous avez mentionnée ici, vous recherchez un utilisateur par ID utilisateur, qui est en quelque sorte la clé de cet utilisateur, dans ce cas, je voudrais probablement lever une exception si aucune instance d'instance d'utilisateur n'est trouvée. .
C'est la règle que je respecte généralement:
la source
Cela variera en fonction du contexte, mais je retournerai généralement null si je recherche un objet particulier (comme dans votre exemple) et retournerai une collection vide si je cherche un ensemble d'objets mais il n'y en a pas.
Si vous avez fait une erreur dans votre code et que le retour de null conduit à des exceptions de pointeur nul, alors le plus tôt sera le mieux. Si vous renvoyez un objet vide, son utilisation initiale peut fonctionner, mais vous pouvez obtenir des erreurs ultérieurement.
la source
Le meilleur dans ce cas renvoie "null" dans un cas où il n'y a pas un tel utilisateur. Rendez également votre méthode statique.
Éditer:
Habituellement, des méthodes comme celle-ci sont membres d'une classe "Utilisateur" et n'ont pas accès à ses membres d'instance. Dans ce cas, la méthode doit être statique, sinon vous devez créer une instance de "User" puis appeler la méthode GetUserById qui retournera une autre instance de "User". D'accord, c'est déroutant. Mais si la méthode GetUserById est membre d'une classe "DatabaseFactory" - pas de problème pour la laisser comme membre d'instance.
la source
Je retourne personnellement une instance par défaut de l'objet. La raison en est que je m'attends à ce que la méthode retourne zéro à plusieurs ou zéro à un (selon le but de la méthode). La seule raison pour laquelle il s'agirait d'un état d'erreur de quelque nature que ce soit, en utilisant cette approche, est que la méthode n'a renvoyé aucun objet (s) et était toujours attendue (en termes de retour un à plusieurs ou singulier).
En ce qui concerne l'hypothèse qu'il s'agit d'une question de domaine commercial - je ne la vois tout simplement pas de ce côté de l'équation. La normalisation des types de retour est une question d'architecture d'application valide. À tout le moins, il est sujet à normalisation dans les pratiques de codage. Je doute qu'il y ait un utilisateur professionnel qui va dire "dans le scénario X, donnez-lui simplement une valeur nulle".
la source
Dans nos objets métier, nous avons 2 principales méthodes Get:
Pour garder les choses simples dans le contexte ou si vous vous posez des questions, ce serait:
La première méthode est utilisée lors de l'obtention d'entités spécifiques, la seconde méthode est utilisée spécifiquement lors de l'ajout ou de la modification d'entités sur des pages Web.
Cela nous permet d'avoir le meilleur des deux mondes dans le contexte où ils sont utilisés.
la source
Je suis un étudiant en informatique français, alors excusez mon pauvre anglais. Dans nos classes, on nous dit qu'une telle méthode ne devrait jamais retourner null, ni un objet vide. L'utilisateur de cette méthode est censé d'abord vérifier que l'objet qu'il recherche existe avant d'essayer de l'obtenir.
En utilisant Java, on nous demande d'ajouter un
assert exists(object) : "You shouldn't try to access an object that doesn't exist";
au début de toute méthode qui pourrait retourner null, pour exprimer la "précondition" (je ne sais pas quel est le mot en anglais).IMO ce n'est vraiment pas facile à utiliser mais c'est ce que j'utilise, en attendant quelque chose de mieux.
la source
Si le cas de l'utilisateur de ne pas être détecté se assez souvent, et que vous voulez faire face à cette de diverses manières en fonction des circonstances (parfois , jeter une exception, en substituant parfois un utilisateur vide) , vous pouvez aussi utiliser quelque chose près de F # de
Option
de ou HaskellMaybe
type , qui sépare explicitement le cas «sans valeur» de «trouvé quelque chose!». Le code d'accès à la base de données pourrait ressembler à ceci:Et être utilisé comme ceci:
Malheureusement, tout le monde semble rouler un type comme celui-ci.
la source
Je retourne généralement null. Il fournit un mécanisme rapide et facile pour détecter si quelque chose a foiré sans lancer d'exceptions et en utilisant des tonnes de try / catch partout.
la source
Pour les types de collection, je retournerais une collection vide, pour tous les autres types, je préfère utiliser les modèles NullObject pour retourner un objet qui implémente la même interface que celle du type de retour. pour plus de détails sur le motif, consultez le texte du lien
En utilisant le modèle NullObject, ce serait: -
{// Imaginez du code ici pour accéder à la base de données .....
}
la source
Pour dire ce que les autres ont dit de manière plus concise ...
Les exceptions concernent des circonstances exceptionnelles
Si cette méthode est une couche d'accès aux données pure, je dirais qu'étant donné un paramètre qui est inclus dans une instruction select, il s'attendrait à ce que je ne trouve aucune ligne à partir de laquelle construire un objet, et donc retourner null serait acceptable car cela est la logique d'accès aux données.
D'un autre côté, si je m'attendais à ce que mon paramètre reflète une clé primaire et que je ne récupère qu'une ligne, si j'en récupérais plus d'une, je lèverais une exception. 0 est ok pour retourner null, 2 n'est pas.
Maintenant, si j'avais un code de connexion vérifié par rapport à un fournisseur LDAP, puis vérifié par rapport à une base de données pour obtenir plus de détails et que je m'attendais à ce qu'ils soient synchronisés à tout moment, je pourrais alors lancer l'exception. Comme d'autres l'ont dit, ce sont des règles commerciales.
Maintenant, je dirai que c'est une règle générale . Il y a des moments où vous voudrez peut-être briser cela. Cependant, mon expérience et mes expériences avec C # (beaucoup de cela) et Java (un peu de cela) m'ont appris qu'il est beaucoup plus coûteux en termes de performances de traiter les exceptions que de gérer les problèmes prévisibles via une logique conditionnelle. Je parle à hauteur de 2 ou 3 ordres de grandeur plus chers dans certains cas. Donc, s'il est possible que votre code se retrouve dans une boucle, je vous conseillerais de retourner null et de le tester.
la source
Pardonnez mon pseudo-php / code.
Je pense que cela dépend vraiment de l'utilisation prévue du résultat.
Si vous souhaitez éditer / modifier la valeur de retour et l'enregistrer, renvoyez un objet vide. De cette façon, vous pouvez utiliser la même fonction pour remplir des données sur un objet nouveau ou existant.
Disons que j'ai une fonction qui prend une clé primaire et un tableau de données, remplit la ligne de données, puis enregistre l'enregistrement résultant dans la base de données. Étant donné que j'ai l'intention de remplir l'objet avec mes données de toute façon, il peut être très avantageux de récupérer un objet vide du getter. De cette façon, je peux effectuer des opérations identiques dans les deux cas. Vous utilisez le résultat de la fonction getter quoi qu'il arrive.
Exemple:
Ici, nous pouvons voir que la même série d'opérations manipule tous les enregistrements de ce type.
Cependant, si l'intention ultime de la valeur de retour est de lire et de faire quelque chose avec les données, je retournerais null. De cette façon, je peux déterminer très rapidement si aucune donnée n'a été retournée et afficher le message approprié à l'utilisateur.
Habituellement, j'attraperai des exceptions dans ma fonction qui récupère les données (afin que je puisse enregistrer les messages d'erreur, etc ...) puis retournerai null directement à partir de la capture. Peu importe l'utilisateur final, quel est le problème, je trouve donc préférable d'encapsuler mon enregistrement / traitement des erreurs directement dans la fonction qui obtient les données. Si vous maintenez une base de code partagée dans une grande entreprise, cela est particulièrement avantageux car vous pouvez forcer la journalisation / la gestion des erreurs, même sur le programmeur le plus paresseux.
Exemple:
Voilà ma règle générale. Cela a bien fonctionné jusqu'à présent.
la source
Question intéressante et je pense qu'il n'y a pas de "bonne" réponse, car cela dépend toujours de la responsabilité de votre code. Votre méthode sait-elle si aucune donnée trouvée ne pose problème ou non? Dans la plupart des cas, la réponse est "non" et c'est pourquoi le retour de la valeur nulle et le fait de laisser l'appelant gérer sa situation est parfait.
Peut-être qu'une bonne approche pour distinguer les méthodes de lancement des méthodes de retour nul est de trouver une convention dans votre équipe: les méthodes qui disent qu'elles "obtiennent" quelque chose devraient lever une exception s'il n'y a rien à obtenir. Les méthodes qui peuvent retourner null peuvent être nommées différemment, peut-être "Find ..." à la place.
la source
Si l'objet retourné est quelque chose qui peut être itéré, je retournerais un objet vide, de sorte que je n'ai pas à tester d'abord null.
Exemple:
la source
J'aime ne pas retourner null à partir de n'importe quelle méthode, mais utiliser le type fonctionnel Option à la place. Les méthodes qui ne peuvent renvoyer aucun résultat renvoient une option vide, plutôt que null.
En outre, ces méthodes qui ne peuvent renvoyer aucun résultat doivent l'indiquer par leur nom. Je place normalement Try ou TryGet ou TryFind au début du nom de la méthode pour indiquer qu'elle peut retourner un résultat vide (par exemple TryFindCustomer, TryLoadFile, etc.).
Cela permet à l'appelant appliquer différentes techniques, comme la collection pipelining (voir Martin Fowler Pipeline Collection ) sur le résultat.
Voici un autre exemple où le retour d'Option au lieu de null est utilisé pour réduire la complexité du code: Comment réduire la complexité cyclomatique: Type fonctionnel d'option
la source
Plus de viande à hacher: disons que mon DAL renvoie un NULL pour GetPersonByID comme conseillé par certains. Que doit faire mon BLL (plutôt fin) s'il reçoit un NULL? Transmettez ce NULL et laissez le consommateur final s'en préoccuper (dans ce cas, une page ASP.Net)? Et si le BLL levait une exception?
Le BLL peut être utilisé par ASP.Net et Win App, ou une autre bibliothèque de classes - je pense qu'il est injuste de s'attendre à ce que le consommateur final "sache" intrinsèquement que la méthode GetPersonByID renvoie un null (sauf si des types null sont utilisés, je suppose ).
Ma prise (pour ce que ça vaut) est que mon DAL renvoie NULL si rien n'est trouvé. POUR CERTAINS OBJETS, ce n'est pas grave - cela pourrait être une liste de 0: beaucoup de choses, donc ne rien avoir c'est bien (par exemple une liste de livres préférés). Dans ce cas, mon BLL renvoie une liste vide. Pour la plupart des choses d'entité unique (par exemple, utilisateur, compte, facture) si je n'en ai pas, c'est définitivement un problème et une exception coûteuse. Cependant, étant donné que la récupération d'un utilisateur par un identifiant unique qui a été précédemment donné par l'application doit toujours renvoyer un utilisateur, l'exception est une exception "correcte", comme dans son cas exceptionnel. Le consommateur final du BLL (ASP.Net, f'rinstance) ne s'attend jamais à ce que les choses soient géniales, donc un gestionnaire d'exceptions non géré sera utilisé au lieu d'envelopper chaque appel unique à GetPersonByID dans un bloc try-catch.
S'il y a un problème flagrant dans mon approche, faites-le moi savoir car je suis toujours désireux d'apprendre. Comme d'autres affiches l'ont dit, les exceptions sont des choses coûteuses, et l'approche «vérifier d'abord» est bonne, mais les exceptions devraient être juste cela - exceptionnelles.
J'apprécie ce post, beaucoup de bonnes suggestions pour les scénarios "ça dépend" :-)
la source
Je pense que les fonctions ne devraient pas retourner null, pour la santé de votre base de code. Je peux penser à quelques raisons:
Il y aura une grande quantité de clauses de garde traitant la référence nulle
if (f() != null)
.Qu'est-ce que c'est
null
, est-ce une réponse acceptée ou un problème? Null est-il un état valide pour un objet spécifique? (imaginez que vous êtes un client du code). Je veux dire que tous les types de référence peuvent être nuls, mais le devraient-ils?Le fait de
null
traîner donnera presque toujours quelques exceptions NullRef inattendues de temps en temps à mesure que votre base de code se développera.Il existe des solutions
tester-doer pattern
ou la mise en œuvre de laoption type
programmation fonctionnelle.la source
Je suis perplexe quant au nombre de réponses (sur le Web) qui disent que vous avez besoin de deux méthodes: une méthode "IsItThere ()" et une méthode "GetItForMe ()", ce qui conduit à une condition de concurrence. Qu'est-ce qui ne va pas avec une fonction qui retourne null, l'affectant à une variable et vérifiant la variable pour Null tout en un test? Mon ancien code C était parsemé de
if (NULL! = (variable = fonction (arguments ...))) {
Vous obtenez donc la valeur (ou null) dans une variable, et le résultat à la fois. Cet idiome a-t-il été oublié? Pourquoi?
la source
Je suis d'accord avec la plupart des articles ici, qui tendent vers
null
.Mon raisonnement est que la génération d'un objet vide avec des propriétés non nullables peut provoquer des bugs. Par exemple, une entité avec une
int ID
propriété aurait une valeur initiale deID = 0
, qui est une valeur entièrement valide. Si cet objet, dans certaines circonstances, devait être enregistré dans la base de données, ce serait une mauvaise chose.Pour tout ce qui a un itérateur, j'utiliserais toujours la collection vide. Quelque chose comme
est une odeur de code à mon avis. Les propriétés de collection ne doivent jamais être nulles.
Un cas de bord est
String
. Beaucoup de gens disent que ceString.IsNullOrEmpty
n'est pas vraiment nécessaire, mais vous ne pouvez pas toujours faire la distinction entre une chaîne vide et null. De plus, certains systèmes de base de données (Oracle) ne les distinguent pas du tout (''
sont stockés sousDBNULL
), vous êtes donc obligé de les gérer également. La raison en est que la plupart des valeurs de chaîne proviennent soit de l'entrée utilisateur ou de systèmes externes, alors que ni les zones de texte ni la plupart des formats d'échange n'ont des représentations différentes pour''
etnull
. Ainsi, même si l'utilisateur souhaite supprimer une valeur, il ne peut rien faire de plus que d'effacer le contrôle d'entrée. De plus, la distinction desnvarchar
champs de base de données nullable et non nullable est plus que discutable, si votre SGBD n'est pas Oracle - un champ obligatoire qui permet''
est bizarre, votre interface utilisateur ne le permettrait jamais, donc vos contraintes ne sont pas mappées. Donc, la réponse ici, à mon avis, est de les gérer également, toujours.Concernant votre question concernant les exceptions et les performances: si vous lancez une exception que vous ne pouvez pas gérer complètement dans la logique de votre programme, vous devez à un moment donné abandonner ce que fait votre programme et demander à l'utilisateur de refaire ce qu'il vient de faire. Dans ce cas, la pénalité de performance d'un
catch
est vraiment le moindre de vos soucis - avoir à demander à l'utilisateur est l'éléphant dans la pièce (ce qui signifie re-restituer toute l'interface utilisateur, ou envoyer du HTML via Internet). Donc, si vous ne suivez pas l'anti-modèle de " Flux de programme avec exceptions ", ne vous embêtez pas, jetez-en un si cela a du sens. Même dans les cas limites, tels que "Exception de validation", les performances ne sont vraiment pas un problème, car vous devez de nouveau demander à l'utilisateur, dans tous les cas.la source
Un modèle TryGet asynchrone:
Pour les méthodes synchrones, je crois que la réponse de @Johann Gerell est le modèle à utiliser dans tous les cas.
Cependant, le modèle TryGet avec le
out
paramètre ne fonctionne pas avec les méthodes Async.Avec Tuple Literals de C # 7, vous pouvez maintenant faire ceci:
la source