Je regardais la présentation d'Anders sur C # 4.0 et un aperçu de C # 5.0 , et cela m'a fait réfléchir à quand les paramètres facultatifs sont disponibles en C #, quelle sera la manière recommandée de déclarer des méthodes qui n'ont pas besoin de tous les paramètres spécifiés?
Par exemple, quelque chose comme la FileStream
classe a une quinzaine de constructeurs différents qui peuvent être divisés en «familles» logiques, par exemple ceux ci-dessous à partir d'une chaîne, ceux de an IntPtr
et ceux de a SafeFileHandle
.
FileStream(string,FileMode);
FileStream(string,FileMode,FileAccess);
FileStream(string,FileMode,FileAccess,FileShare);
FileStream(string,FileMode,FileAccess,FileShare,int);
FileStream(string,FileMode,FileAccess,FileShare,int,bool);
Il me semble que ce type de modèle pourrait être simplifié en ayant trois constructeurs à la place, et en utilisant des paramètres optionnels pour ceux qui peuvent être par défaut, ce qui rendrait les différentes familles de constructeurs plus distinctes [note: je sais que ce changement ne sera pas fait dans la BCL, je parle hypothétiquement pour ce type de situation].
Qu'est-ce que tu penses? À partir de C # 4.0, sera-t-il plus logique de créer des groupes de constructeurs et de méthodes étroitement liés en une seule méthode avec des paramètres optionnels, ou y a-t-il une bonne raison de s'en tenir au mécanisme traditionnel de surcharge multiple?
la source
Lorsqu'une surcharge de méthode effectue normalement la même chose avec un nombre différent d'arguments, les valeurs par défaut seront utilisées.
Lorsqu'une surcharge de méthode exécute une fonction différemment en fonction de ses paramètres, la surcharge continuera à être utilisée.
J'ai utilisé l'option facultative dans mes jours VB6 et je l'ai ratée depuis, cela réduira beaucoup de duplication de commentaires XML en C #.
la source
J'utilise Delphi avec des paramètres optionnels depuis toujours. Je suis plutôt passé à l'utilisation de surcharges.
Parce que lorsque vous allez créer plus de surcharges, vous serez invariablement en conflit avec un formulaire de paramètre facultatif, et vous devrez alors les convertir en non-facultatif de toute façon.
Et j'aime l'idée qu'il y a généralement une super méthode, et les autres sont des wrappers plus simples autour de celle-là.
la source
Foo(A, B, C)
exigeFoo(A)
,Foo(B)
,Foo(C)
,Foo(A, B)
,Foo(A, C)
,Foo(B, C)
.J'utiliserai certainement la fonctionnalité de paramètres optionnels de 4.0. Il se débarrasse du ridicule ...
... et place les valeurs là où l'appelant peut les voir ...
Beaucoup plus simple et beaucoup moins sujet aux erreurs. J'ai en fait vu cela comme un bogue dans le cas de surcharge ...
Je n'ai pas encore joué avec le complicateur 4.0, mais je ne serais pas choqué d'apprendre que le complieur émet simplement les surcharges pour vous.
la source
Les paramètres facultatifs sont essentiellement un élément de métadonnées qui dirige un compilateur qui traite un appel de méthode pour insérer les valeurs par défaut appropriées sur le site d'appel. En revanche, les surcharges fournissent un moyen par lequel un compilateur peut sélectionner l'une des nombreuses méthodes, dont certaines peuvent fournir elles-mêmes des valeurs par défaut. Notez que si l'on essaie d'appeler une méthode qui spécifie des paramètres optionnels à partir de code écrit dans un langage qui ne les prend pas en charge, le compilateur exigera que les paramètres "optionnels" soient spécifiés, mais comme appeler une méthode sans spécifier de paramètre optionnel est équivalent à l'appeler avec un paramètre égal à la valeur par défaut, il n'y a aucun obstacle à ce que ces langages appellent de telles méthodes.
Une conséquence importante de la liaison de paramètres facultatifs sur le site d'appel est qu'ils se verront attribuer des valeurs basées sur la version du code cible qui est disponible pour le compilateur. Si un assembly
Foo
a une méthodeBoo(int)
avec une valeur par défaut de 5 et que l'assemblyBar
contient un appel àFoo.Boo()
, le compilateur le traitera comme unFoo.Boo(5)
. Si la valeur par défaut est modifiée à 6 et l'assemblyFoo
est recompilé,Bar
continuera à appeler àFoo.Boo(5)
moins ou jusqu'à ce qu'il soit recompilé avec cette nouvelle version deFoo
. Il faut donc éviter d'utiliser des paramètres optionnels pour des choses qui pourraient changer.la source
void Foo(int value) … void Foo() { Foo(42); }
. De l'extérieur, l'appelant ne sait pas quelle valeur par défaut est utilisée, ni quand elle peut changer; il faudrait surveiller la documentation écrite pour cela. Les valeurs par défaut pour les paramètres facultatifs peuvent être vues comme cela: documentation-in-code quelle est la valeur par défaut.On peut se demander si des arguments optionnels ou des surcharges doivent être utilisés ou non, mais surtout, chacun a sa propre zone où il est irremplaçable.
Les arguments facultatifs, lorsqu'ils sont utilisés en combinaison avec des arguments nommés, sont extrêmement utiles lorsqu'ils sont combinés avec certaines listes d'arguments longues avec toutes les options d'appels COM.
Les surcharges sont extrêmement utiles lorsque la méthode est capable d'opérer sur de nombreux types d'arguments différents (juste un des exemples), et effectue des castings en interne, par exemple; vous le nourrissez simplement avec n'importe quel type de données qui a du sens (qui est accepté par une surcharge existante). Je ne peux pas battre ça avec des arguments optionnels.
la source
J'attends avec impatience les paramètres optionnels car ils gardent les valeurs par défaut plus proches de la méthode. Ainsi, au lieu de dizaines de lignes pour les surcharges qui appellent simplement la méthode "développée", vous ne définissez qu'une seule fois la méthode et vous pouvez voir ce que les paramètres optionnels par défaut dans la signature de méthode. Je préfère regarder:
Au lieu de cela:
Évidemment, cet exemple est vraiment simple mais dans le cas de l'OP avec 5 surcharges, les choses peuvent devenir très vite encombrées.
la source
L'un de mes aspects préférés des paramètres facultatifs est que vous voyez ce qui arrive à vos paramètres si vous ne les fournissez pas, même sans passer à la définition de la méthode. Visual Studio vous montrera simplement la valeur par défaut du paramètre lorsque vous tapez le nom de la méthode. Avec une méthode de surcharge, vous êtes coincé soit avec la lecture de la documentation (si elle est même disponible), soit avec la navigation directe vers la définition de la méthode (si disponible) et vers la méthode que la surcharge encapsule.
En particulier: l'effort de documentation peut augmenter rapidement avec la quantité de surcharges, et vous finirez probablement par copier des commentaires déjà existants à partir des surcharges existantes. C'est assez ennuyeux, car cela ne produit aucune valeur et casse le principe DRY ). D'un autre côté, avec un paramètre facultatif, il y a exactement un endroit où tous les paramètres sont documentés et vous voyez leur signification ainsi que leurs valeurs par défaut lors de la saisie.
Enfin, si vous êtes le consommateur d'une API, vous n'aurez peut-être même pas la possibilité d'inspecter les détails de l'implémentation (si vous n'avez pas le code source) et n'avez donc aucune chance de voir à quelle super méthode les surchargées emballent. Ainsi, vous êtes coincé avec la lecture du document et espérez que toutes les valeurs par défaut y sont répertoriées, mais ce n'est pas toujours le cas.
Bien sûr, ce n’est pas une réponse qui traite tous les aspects, mais je pense qu’elle en ajoute une qui n’a pas encore été abordée.
la source
Bien qu'il existe (supposément?) Deux moyens conceptuellement équivalents disponibles pour modéliser votre API à partir de zéro, ils présentent malheureusement une différence subtile lorsque vous devez considérer la compatibilité descendante d'exécution pour vos anciens clients dans la nature. Mon collègue (merci Brent!) M'a signalé ce merveilleux article: Problèmes de versioning avec des arguments optionnels . Certains en citent:
la source
Une mise en garde des paramètres facultatifs est la gestion des versions, où un refactor a des conséquences inattendues. Un exemple:
Code initial
Supposons que c'est l'un des nombreux appelants de la méthode ci-dessus:
Ici, l'événement n'est pas silencieux et est traité comme critique.
Maintenant, disons qu'après un refactor, nous constatons que toutes les erreurs invitent de toute façon l'utilisateur, donc nous n'avons plus besoin de l'indicateur silencieux. Alors on l'enlève.
Après refactor
L'ancien appel se compile toujours, et disons qu'il glisse dans le refactor sans changement:
Maintenant
false
aura un effet involontaire, l'événement ne sera plus traité comme critique.Cela pourrait entraîner un défaut subtil, car il n'y aura pas d'erreur de compilation ou d'exécution (contrairement à d'autres mises en garde d'options, telles que ceci ou cela ).
Notez qu'il existe de nombreuses formes de ce même problème. Un autre formulaire est décrit ici .
Notez également que strictement l' utilisation des paramètres nommés lors de l' appel de la méthode évitera la question, par exemple comme ceci:
HandleError("Disk is full", silent:false)
. Cependant, il peut ne pas être pratique de supposer que tous les autres développeurs (ou utilisateurs d'une API publique) le feront.Pour ces raisons, j'éviterais d'utiliser des paramètres optionnels dans une API publique (ou même une méthode publique si elle pouvait être largement utilisée) à moins qu'il n'y ait d'autres considérations convaincantes.
la source
Les deux paramètres facultatifs, la surcharge de méthode ont leur propre avantage ou inconvénient. Cela dépend de votre préférence pour choisir entre eux.
Paramètre facultatif: disponible uniquement dans .Net 4.0. paramètre facultatif réduit la taille de votre code. Vous ne pouvez pas définir le paramètre out et ref
méthodes surchargées: vous pouvez définir les paramètres de sortie et de référence. La taille du code augmentera mais les méthodes surchargées sont faciles à comprendre.
la source
Dans de nombreux cas, des paramètres facultatifs sont utilisés pour changer d'exécution. Par exemple:
Le paramètre Discount ici est utilisé pour alimenter l'instruction if-then-else. Il y a le polymorphisme qui n'a pas été reconnu, puis il a été implémenté comme une instruction if-then-else. Dans de tels cas, il est préférable de diviser les deux flux de contrôle en deux méthodes indépendantes:
De cette façon, nous avons même protégé la classe de recevoir un appel sans remise. Cet appel signifierait que l'appelant pense qu'il y a une réduction, mais en fait, il n'y a pas de réduction du tout. Un tel malentendu peut facilement provoquer un bug.
Dans des cas comme celui-ci, je préfère ne pas avoir de paramètres optionnels, mais pour forcer l'appelant à sélectionner explicitement le scénario d'exécution qui convient à sa situation actuelle.
La situation est très similaire à celle des paramètres qui peuvent être nuls. C'est également une mauvaise idée lorsque la mise en œuvre se résume à des déclarations comme
if (x == null)
.Vous pouvez trouver une analyse détaillée sur ces liens: éviter les paramètres facultatifs et éviter les paramètres nuls
la source
Pour ajouter une évidence quand utiliser une surcharge au lieu des options:
Chaque fois que vous avez un certain nombre de paramètres qui n'ont de sens qu'ensemble, n'y introduisez pas d'options.
Ou plus généralement, chaque fois que vos signatures de méthode activent des modèles d'utilisation qui n'ont pas de sens, limitez le nombre de permutations d'appels possibles. Par exemple, en utilisant des surcharges au lieu des options (cette règle est également vraie lorsque vous avez plusieurs paramètres du même type de données, d'ailleurs; ici, des périphériques comme les méthodes d'usine ou les types de données personnalisés peuvent aider).
Exemple:
la source