Bonjour,
J'ai un constructeur qui prend un argument.
Si la valeur de cet argument n'est pas valide, que puis-je faire ?
Jeter une exception ? Pas d'autres solution ?
Version imprimable
Bonjour,
J'ai un constructeur qui prend un argument.
Si la valeur de cet argument n'est pas valide, que puis-je faire ?
Jeter une exception ? Pas d'autres solution ?
Bonjour,
Jeter une exception dans un constructeur est une très mauvaise pratique. En C#, ça va parce que tout est géré par le framework mais dans certains langages comme c++ ça peut être très problématique car tu peux te retrouver avec des objets incomplets.
Le meilleur à faire dans ton cas serait de créer une Factory qui vérifie ton argument et renvoie ton objet. Je te laisse googler ce design pattern afin d'en savoir plus mais il n'y a rien d'extrèmement compliqué :)
Je voudrais faire entendre un son de cloche différent. Lancer des exceptions depuis un constructeur ne pose aucun problème et, d'ailleurs, plusieurs types du framework font cela. Il ne faut donc surtout pas hésiter à mes yeux, surtout s'il ne s'agit que de quelques vérifications.
Cela dit, attention tout de même ! Si une exception est lancée dans le constructeur, il est vrai que l'objet ne sera pas accessible par le code qui instanciait, ce qui est bien (même en cas de bloc using sur un type implémentant IDisposable : on n'entrera jamais dans le bloc et Dispose ne sera jamais appelé). En revanche, l'instance a bien été créée et reste accessible par le seul GC qui appellera le destructeur. C'est la grosse différence avec le C++. Gaffe, donc, a bien écrire la destructeur avec cette perspective en tête.
Enfin, utiliser une méthode factory statique (MyType.Create()) me semble toutefois acceptable dans le cas d'un processus de création particulièrement coûteux (temps CPU, ressources systèmes, etc). Non pas que cela poserait problème si c'était fait dans un constructeur mais on attend intuitivement d'un constructeur qu'il soit léger. Garder le constructeur privé et fournir une telle méthode statique est donc une simple indication à l'usage des consommateurs de la classe. Mais c'est lourd et en abuser est à mes yeux un anti-pattern. Je garde plutôt ces méthodes pour les cas où je préfère renvoyer null plutôt que de lever une exception où, bien sûr, pour tous les cas où le pattern factory est approprié.
PS : MSDN est toujours une bonne source : Constructor design guidelines
salut
vu que ton constructeur prends un argument, rien n'empeche ton objet après construction de positionner un "état - Je suis pas bien construit"...
Après, en théorie, si la construction de ton objet est impossible parce que un argument contient une mauvaise valeur, alors il vaut peut-etre mieux une factory...
Ou alors, construire ton objet et avoir une méthode dans laquelle tu passes ton argument et qui te retournera un boolean pour dire si les choses à construire à partir de cet argument se sont construites :)
ne pas répondre sur un langage avec des arguments d'un autre langage ...
et pour compléter DonQuiche que je puissoie, en général le throw est fait au début du constructeur, (if arg is null {throw argumentnullexception} par exemple) donc ça ne pose pas de soucis niveau mémoire
salut
moi, comme je suis un peu neuneu, ce que je fais est un peu ce que fait Microsoft...
Prenons l'exemple de la classe SerialPort qui permet de faire de la communication série. Si on regarde cette classe, dans le constructeur, on peut passer des arguments... Par contre, ce sont les méthodes de "communication" qui elles vont retourner une exception ou un boolean pour dire "Pas de Port Série".
Et pour moi, c'est plutot logique...
Pour moi, une classe devrait toujours pouvoir être construite. Par contre, si défaut d'argument, de ressources disponibles il y a , alors, les méthodes "obligatoires" de cette classe te le diront dès le premier appel.
J'ai JAMAIS au cours de ma courte carrière de développeur (27 ans de dév :)) eu une situation ou je me suis dit "Tiens, je vais lancer une exception dans mon constructeur"...
Peut-etre parce que je privilégie les choses simples aux choses compliquées, alambiquées, divines au niveau design mais impossible à réaliser dans le temps imparti.. Peut-etre parce que je pense qu'un logiciel doit avant tout apporter la réponse au besoin avant d'être "magnifique" d'un point de vue architecture...
En tout cas, même si la question est pertinente et interessante, j'avoue ne jamais avoir eu à traiter ce type de cas...
Peut-etre que je crois trop que l'informatique, c'est TOUJOURS simple...
Ma devise : "En informatique, rien n'est compliqué. Par contre, parfois, résoudre un problème peut-etre complexe"... :)
je ne suis pas trop d'accord
en général dans une application on log les erreurs pour pouvoir débugger rapidement
si une classe demande un paramètre dans son constructeur et qu'elle en a réellement besoin, ca me parait plus logique de thrower tout de suite, au moins dans le stacktrace on saurait à quel moment le paramètre était null
car les objets peuvent venir de loin ... et si tu throw lors d'un appel à une méthode, tu ne sauras pas forcément à quel moment tu as instancié ta classe et donc le debug en sera moins aisé
CA ça me parait logique !
après si le paramètre n'est pas nécessaire au moment de la construction et peut etre fourni plus tard pas de soucis
Je suis aussi de cet avis,Citation:
Envoyé par Pol63
Si un constructeur ou une méthode ne peut résoudre un problème,
Il est de son devoir d'en avertir au plus vite l'objet appelant ...
L'objet sera forcement créé mais seul le Garbage aura un pointeur dessus
(Ce qu'on appelle un «constructeur» est en fait un «initialisateur» ... La construction a déjà été faite avant ...
Dans certains langages, il est possible de faire son propre constructeur (ex: python))
Si le fait d'avoir un objet inutilisable qui traine au niveau du Garbage te gène,
(parce qu'il est trop lourd, et/ou parce qu'il y en aura peut être beaucoup)
Alors une Factory te sortirait de là ^^.
L'informatique généralement c'est simple, à échelle micro, par contre, à échelle macro, ça ne l'est pas toujours.
Et quand tu développes une solution IT qui répond à un besoin métier complexe, ta solution IT peut devenir complexe par extension (ou propagation...).
Du coup, oui, quand tu fais une solution IT pour un besoin IT, ça me parait totalement justifié d'avoir recours à ce genre de pratique :
Car dans ce cas, quelqu'un avec la compréhension métier de ton application qui doit un jour comprendre les mécanismes utilisés comprendra la raison.
Par contre, dans le case d'une solution IT pour un besoin business, ça me parait totalement justifié d'avoir recours à ce genre de pratique là :
Qui se traduirait par exemple par un :Citation:
Envoyé par Pol63
Car dans ce cas, tu sais que telle création n'a pas fonctionné, c'est aussi simple que ça. Tu décomposes ainsi de façon plus "macro".Code:
1
2
3
4
5 try { CreateCommand(product, customer); } catch(CommandCreationException cce) { /* do something */ }
Du coup, j'aurais tendance à dire que vous avez tous raison, mais que c'est le contexte d'application qui fait la différence.
He bien... Je ne pensais pas déclencher autant de passion avec ma question.
Toutes vos réponses sont intéressantes, mais j'hésite encore sur la solution que je vais choisir... car mon client n'est pas très doué en code (moi, c'est seulement en C#, que je ne suis pas doué :lol:), et je le vois bien grimacer si je mets en place quelque chose qui lui paraît compliqué.
Enfin bon. Vous m'avez donné toutes les cartes, et je vous en remercie.
le plus simple pour un développeur tiers doit être le throw dans le constructeur
le design pattern factory n'est pas forcément connu de tous
et avoir l'erreur tout de suite à l'instanciation est sans doute plus logique plutot que de se dire "putain telle méthode elle marche pas"
De ce que j'en comprends, il faudrait que ton code soit plutôt orienté fonctionnel pour être compris, et donc la solution du Throw dans le constructeur me semble plus appropriée comme je l'ai expliqué plus haut.
Maintenant, l'autre hypothèse est également valable, mais je conseillerai de bien la documenter.
Je comprends toutes vos remarques :)
Je me demande juste si il existe des classes dans le framework .Net qui ont ce genre de comportement ?
Je serais curieux de savoir si des classes accessibles aux développeurs .Net retournent une exception à la construction ?
Franchement, j'aimerais bien un exemple dans le framework si cela existe :)
rien que pour un throw new argumentnullexception si arg is null dans le constructeur (pas regardé pour d'autres types d'erreur) :
non accessible il y en a énormément
accessible il y en a plein aussi dont quelques exemples :
new system.drawing.pen(brush)
new System.IO.FileInfo(filepath)
new System.Net.IPAddress(byte())
etc...
CHALLENGE ACCEPTED! :D
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 public StreamWriter(string path, bool append, Encoding encoding, int bufferSize) : base(null) { if (path == null) { throw new ArgumentNullException("path"); } if (encoding == null) { throw new ArgumentNullException("encoding"); } if (path.Length == 0) { throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); } Stream stream = StreamWriter.CreateFile(path, append); this.Init(stream, encoding, bufferSize); }
devant tant de connaissances, je ne peux qu'applaudir et m'incliner bien bas :)
Tu peux prendre un décompileur comme JustDecompile (ou Reflector) pour aller explorer un peu :)
Cela dit, même sur les pages des constructeurs on peut voir qu'ils lèvent des exceptions:
http://msdn.microsoft.com/en-us/library/3aadshsx.aspx
J'ai honte :).. je me cache
Merci Nathanael... en tout cas !!!
Oui, en fait il y a beaucoup de classes du framework qui lèvent des exceptions dans le constructeur. Logique puisque, comme montré dans le lien MSDN que j'ai donné, c'est une des bonnes pratiques recommandées par MS.
@theMonz31
Et pourquoi donc une classe devrait-elle toujours pouvoir être construite ? En raison de quel argument théologique ? Si cette classe est inutile dans cet état incorrect, c'est toi qui complexifie le code, sa compréhension et son débogage en retardant les vérifications. ;)
Il n'y a aucune bonne raison de refuser dans l'absolu les levées d'exception dans les constructeurs. Et utiliser des méthodes plus ou moins tordues (le flag isInvalid... Beurk !) pour l'éviter ne peut que complexifier et obscurcir les choses. Le seul cas où on devrait accepter de créer un objet dans un état incorrect est le cas où les consommateurs peuvent précisément avoir besoin de manipuler et échanger un état incorrect.
@Nathanael
Petit hors-sujet, j'ai une petite préférence pour ILSpy personnellement. Ça me semble la meilleure alternative à Reflector désormais, ne serait-ce que parce qu'il offre un rendu C# et IL là où JustDecompile n'offre que le C#.