C# paramètres facultatifs sur les méthodes de remplacement
Semble que dans .NET Framework il y a un problème avec les paramètres facultatifs lorsque vous remplacez la méthode. La sortie du code ci-dessous:
"bbb"
"aaa"
. Mais le résultat que j'attends est:
"bbb"
"bbb"
.Est-il une solution pour ce. Je sais que cela peut être résolu avec la surcharge de méthode, mais vous demandez la raison pour cela. Le code fonctionne très bien en Mono.
class Program
{
class AAA
{
public virtual void MyMethod(string s = "aaa")
{
Console.WriteLine(s);
}
public virtual void MyMethod2()
{
MyMethod();
}
}
class BBB : AAA
{
public override void MyMethod(string s = "bbb")
{
base.MyMethod(s);
}
public override void MyMethod2()
{
MyMethod();
}
}
static void Main(string[] args)
{
BBB asd = new BBB();
asd.MyMethod();
asd.MyMethod2();
}
}
- Les paramètres facultatifs sont mauvais, mmkay! TBH, jamais je n'ai même pris la peine de les utiliser.
- Il est très usfull fonctionnalité. Rejeter une fonctionnalité dans tous les scénarios n'est pas sage.
- Je suis curieux de savoir si ce qui se passe dans VB.Net aussi et n'est pas spécifique à compilateur C#..aurez besoin de vérifier..
- Cela ressemble à un bug dans le Mono.
- comment alors? nous avons ici un exemple, lorsqu'il est utilisé en dehors de la classe, le substituée version est inspecté par défaut, et à l'intérieur de la classe des base version est inspecté pour la valeur par défaut, qui n'est pas cohérent
- En fait, lors de l'examen, les deux comportements semblent raisonnables.
- avez-vous vu des avertissements du compilateur tandis qu'au-dessus de code a été compilé?
- À l'aide de C# 4.0 (à partir de Microsoft, le niveau d'Avertissement 4 activé), je n'ai pas.
- Je suis sûr qu'il y a de bonnes raisons de l'utiliser. Mais je suis encore à trouver un tel cas. Normalement (lire idéalement), vous voulez que votre défaut d'être de tout type et pas seulement un très sous-ensemble limité de types (aka primitives et de quelques autres). PS: je n'ai pas dit "je suis le rejet de la fonctionnalité dans tous les scénarios", vous ne savez pas comment vous en êtes arrivé à cette conclusion.
- Veuillez tester avec les deux valeurs par défaut étant le même. Je soupçonne une incompatibilité de signature. Qui serait de tout coeur de le sucer....
- Voir aussi stackoverflow.com/questions/241134/...
- "Je n'ai pas dit" je suis le rejet de la fonctionnalité dans tous les scénarios'" -- bien sûr, vous l'avez fait. "vous ne savez pas comment vous en êtes venu à cette conclusion" -- euh, "paramètres Facultatifs sont mauvais, mmkay!"
Vous devez vous connecter pour publier un commentaire.
Une chose à noter ici est que la version de remplacement est appelée à chaque fois. Modifier le remplacement:
Et la sortie est:
Une méthode dans une classe peut faire un ou deux des éléments suivants:
Il ne peut pas faire les deux, comme une méthode abstraite ne s'occupe que de l'ancien.
Dans
BBB
l'appelMyMethod()
appelle une méthode défini dansAAA
.Car il y a une substitution dans
BBB
, en appelant cette méthode aboutit à une mise en œuvre dansBBB
appelé.Maintenant, la définition de l'
AAA
informe le code appelant, de deux choses l'une (enfin, quelques autres encore qui n'ont pas d'importance ici).void MyMethod(string)
."aaa"
et, par conséquent, lors de la compilation d'un code de la formeMyMethod()
si aucune méthode d'appariementMyMethod()
peut être trouvé, vous pouvez le remplacer par un appel à " MyMethod("aaa").Donc, c'est ce que l'appel en
BBB
n': Le compilateur voit un appel àMyMethod()
, ne trouve pas une méthodeMyMethod()
mais n'trouver une méthodeMyMethod(string)
. Il voit aussi qu'à l'endroit où il a été défini il y a une valeur par défaut de "aaa", donc au moment de la compilation, ça change de ce à un appel àMyMethod("aaa")
.De l'intérieur
BBB
,AAA
est considéré comme le lieu oùAAA
's méthodes sont définies, même si substituée dansBBB
, de sorte qu'ils peut être ignoré.Au moment de l'exécution,
MyMethod(string)
est appelée avec l'argument "aaa". Parce qu'il n'y est substituée à la forme, qui est la forme dite, mais il n'est pas appelé à "bbb", parce que la valeur n'a rien à voir avec le moment de l'exécution de la mise en œuvre, mais avec le moment de la compilation définition.Ajoutant
this.
les changements de définition qui est examiné, et donc les changements que argument est utilisé dans l'appel.Edit: Pourquoi ce qui semble le plus intuitif pour moi.
Personnellement, et depuis que je suis à parler de ce qui est intuitif, il ne peut être personnel, je trouve cela plus intuitive pour la raison suivante:
Si j'étais codage
BBB
alors de savoir si l'appelant ou de la transgression deMyMethod(string)
, je pense, en tant que "faireAAA
truc", c'estBBB
s prendre à "faireAAA
trucs", mais elle faitAAA
choses tout de même. Par conséquent, si l'appel ou de la transgression, je vais être conscient du fait qu'il a étéAAA
que définiMyMethod(string)
.Si j'étais le code appelant, qui a utilisé
BBB
, je pense de "l'aideBBB
trucs". Je ne pourrais pas être très au courant de ce qui a été défini dansAAA
, et j'aurais peut-être penser à cela comme un simple détail de l'implémentation (si je n'ai pas utiliser leAAA
interface à proximité).Le compilateur du comportement correspond à mon intuition, c'est pourquoi lors de la première lecture de la question, il me semblait que les Mono avait un bug. Après réflexion, je ne vois pas comment, soit remplit les comportements spécifiques, mieux que les autres.
Pour que la matière même si, tout en restant à un niveau personnel, je n'avais jamais utiliser les paramètres facultatifs dans l'abstrait, virtuel ou de méthodes de remplacement, et si une substitution de quelqu'un d'autre qui l'a fait, je serais match leur.
MyMethod()
est telle bizarre code quelconque comportement est justifié.Vous pouvez lever l'ambiguïté en appelant le:
(en
MyMethod2()
)Si c'est un bug est difficile; il n'a pas l'air incohérent, si. Resharper vous avertit tout simplement pas avoir des changements à la valeur par défaut dans une substitution, si ça peut aider ;p bien sûr, resharper aussi vous indique la
this.
est redondant, et vous propose de supprimer pour vous ... ce qui modifie le comportement de sorte resharper n'est pas parfait.Il ne ressemble, il pourrait qualifier comme un compilateur bug, je vous l'accorde. J'avais besoin de regarder vraiment avec soin pour être sûr... où est Eric quand vous avez besoin de lui, hein?
Edit:
Le point clé ici est la langue spec; regardons §7.5.3:
(et de fait, §7.4 clairement omet
override
méthodes de l'examen)Il y a un conflit ici.... il affirme que le base méthodes ne sont pas utilisées lorsqu'il y a une méthode dans une classe dérivée - ce qui nous conduirait à la dérivés méthode, mais en même temps, il est dit que les méthodes marqué
override
ne sont pas considérées.Mais, §7.5.1.1 puis déclare:
et puis, §7.5.1.2 explique comment les valeurs sont évaluées au moment de l'invoquer:
Explicitement souligne que c'est en regardant la liste d'arguments, qui a déjà été défini dans le §7.5.1.1 comme venant de la plus spécifique de la déclaration ou de la remplacer. Il semble raisonnable que c'est la "déclaration de méthode" qui est visé dans le §7.5.1.2, donc la valeur transmise doit être de la plus dérivée à le type statique.
Cela pourrait suggérer: le scc a un bug, et il devrait être à l'aide de la dérivés version ("bbb bbb"), sauf s'il est restreint (via
base.
, ou de coulée de base-type) à la recherche à la base des déclarations de méthode (§7.6.8).this.
et changement ReSharper du paramètre par défaut.MyMethod()
, mais le comportement dans ce cas ne correspond pas à la ci-dessus.Cela ressemble à un bug pour moi. Je crois qu'il est bien spécifié,
et qu'il devrait se comporter de la même manière que si vous appelez la
méthode explicite
this
préfixe.J'ai simplifié l'exemple d'utiliser seulement une unique virtuel
méthode, et de montrer à la fois ce qui est appelé et la mise en œuvre
ce que la valeur du paramètre est:
De sorte que tous nous avons besoin de s'inquiéter sont les trois appels dans un délai de RunTests.
Les choses importantes de la spec pour les deux premiers appels sont section
7.5.1.1, qui parle de la liste des paramètres à être utilisé lors de la recherche des paramètres correspondants:
Et de l'article 7.5.1.2:
Le "correspondant paramètre optionnel" est le bit qui lie 7.5.2 pour 7.5.1.1.
Pour les deux
M()
etthis.M()
, ce paramètre liste devrait êtrel'un dans
Derived
comme type statique du receveur estDerived
,En effet, vous pouvez dire que le compilateur traite
que la liste des paramètres plus tôt dans la compilation, comme si vous
rendre le paramètre obligatoire dans
Derived.M()
, les deux de l'les appels de l'échec de sorte que le
M()
appel nécessite le paramètre d'avoirune valeur par défaut dans
Derived
, mais alors l'ignore!En effet, il y a pire: si vous fournissez une valeur par défaut pour le
paramètre dans
Derived
mais le rendre obligatoire dansBase
, l'appelM()
finit à l'aide denull
que la valeur de l'argument. Si rien d'autre,Je dirais que prouve qu'il s'agit d'un bug: que
null
valeur ne peut pas venirà partir de n'importe où valide. (C'est
null
en raison de celle qui est la valeur par défautla valeur de la
string
type; il est toujours juste utilise la valeur par défautpour le type de paramètre.)
Section 7.6.8 de la spécification traite avec la base.M(), qui dit que
ainsi que la non-virtuel le comportement, l'expression est considérée comme
comme
((Base) this).M()
; il est donc tout à fait correct pour la méthode de baseà être utilisés pour déterminer l'efficacité de la liste des paramètres. Cela signifie que
la dernière ligne est correcte.
Juste pour rendre les choses plus facile pour ceux qui veulent voir le très étrange bogue décrit ci-dessus, lorsqu'une valeur n'est spécifié nulle part est utilisée:
this.
, et l'obligatoire/facultatif bizarreries que vous mettez en surbrillance: tous sont sérieusement bizarre et très rompu à la recherche.Avez-vous essayé:
Donc en fait vous dites à votre programme d'utilisation de la Méthode surchargée.
this
est implicite. Donc votre réponse est redondant.base
.Le comportement est certainement très étrange; il n'est pas clair pour moi si il est en fait un bug du compilateur, mais il pourrait être.
Le campus a obtenu une bonne quantité de neige la nuit dernière et Seattle n'est pas très bonne sur la façon de traiter avec de la neige. Mon bus n'est pas exécuté ce matin, donc je ne vais pas être en mesure d'entrer dans le bureau de comparer ce que le C# 4 C# 5 et Roslyn dire à propos de ce cas et si ils sont en désaccord. Je vais essayer de poster une analyse plus tard cette semaine, une fois que je suis de retour dans le bureau et peut utiliser les bons outils de débogage.
M()
de la classe dérivée, puis réussit, mais utilisenull
que la valeur de l'argument!Peut-être que c'est en raison de l'ambiguïté et le compilateur est en donnant la priorité à la base/super-classe. Le dessous de modifier le code de votre classe BBB avec l'ajout de la référence à
this
mot-clé, donne de la sortie "bbb bbb":L'une des choses qu'il implique est que vous devriez toujours utiliser la
this
mot-clé à chaque fois que vous appelez des propriétés ou des méthodes sur l'instance courante de la classe comme un meilleures pratiques.Je serais inquiet si cette ambiguïté dans la base et de l'enfant de la méthode n'a même pas lever un avertissement du compilateur (si pas d'erreur), mais si c'est le cas alors c'était invisible, je suppose.
==================================================================
EDIT: Envisager l'exemple ci-dessous des extraits de ces liens:
http://geekswithblogs.net/BlackRabbitCoder/archive/2011/07/28/c.net-little-pitfalls-default-parameters-are-compile-time-substitutions.aspx
http://geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/c-optional-parameters---pros-and-pitfalls.aspx
Écueil: valeurs de paramètres Facultatifs sont au moment de la compilation
Il y a une chose et une seule chose à garder à l'esprit lors de l'utilisation de paramètres optionnels. Si vous gardez une chose à l'esprit, les chances sont que vous peut bien comprendre et d'éviter les pièges potentiels avec leur utilisation:
Une seule chose est ceci: paramètres facultatifs sont au moment de la compilation, syntaxiques sucre!
Écueil: Méfiez-vous des Paramètres par Défaut de l'Héritage et de l'Implémentation de l'Interface
Maintenant, la deuxième les pièges potentiels de a à faire avec l'héritage et l'implémentation de l'interface. Je vais illustrer avec un puzzle:
Ce qui se passe? Ainsi, même si l'objet, dans chaque cas, est SubTag dont la balise est “SubTag”, vous obtiendrez:
1: SubTag
2: BaseTag
3: ITag
Mais n'oubliez pas de assurez-vous de:
Ne pas insérer de nouveaux paramètres par défaut au moyen d'un ensemble de paramètres par défaut, cela peut entraîner un comportement imprévisible qui ne peuvent pas nécessairement jeter une erreur de syntaxe – ajouter à la fin de la liste ou créer de nouvelles méthode.
Soyez extrêmement prudent lorsque vous utilisez les paramètres par défaut dans les hiérarchies d'héritage et interfaces – choisir le niveau le plus approprié pour ajouter les valeurs par défaut basé sur l'utilisation prévue.
==========================================================================
this
est implicite (jamais l'esprit de ce caprice).this.
rend le code moins lisible et plus difficile à comprendre. Modification des paramètres facultatifs sur les remplacements est déjà reconnu par certains outils comme pas de meilleure pratique, et qui semble une sage chose à interdire (en effet, les paramètres facultatifs abstrait, virtuel ou de méthodes de remplacement paraît imprudent pour moi).Je pense que c'est parce que ces valeurs par défaut sont fixés au moment de la compilation. Si vous utilisez un réflecteur, vous verrez la suite pour MyMethod2 à BBB.
D'accord en général avec @Marc Gravel.
Cependant, je tiens à mentionner que la question est assez vieux en C++ monde (http://www.devx.com/tips/Tip/12737), et la réponse ressemble à "contrairement à des fonctions virtuelles, qui sont résolues au moment de l'exécution, les arguments par défaut sont réglés de manière statique, qui est, lors de la compilation." Donc, ce compilateur C# comportement avait plutôt été accepté délibérément en raison de la cohérence, malgré sa surprise, il semble.
De Toute Façon, Il A Besoin D'Un Correctif
Je n'hésiterais pas à le considérer comme un bug, soit parce que le résultat est mauvais ou si les résultats sont alors le compilateur ne doit pas vous permettre de le déclarer comme "remplacer, ou au moins de donner un avertissement.
Je vous recommande de le signaler à Microsoft.Connectez
Mais Est-Il Bon Ou Mauvais?
Toutefois quant à savoir si c'est le comportement attendu ou non, laissez-nous d'abord analyser les deux points de vue sur elle.
considérons que nous avons le code suivant:
Il y a deux façons de la mettre en œuvre:
Que les arguments optionnels sont traités comme des fonctions surchargées, entraînant la suivante:
Que la valeur par défaut est incorporé dans l'appelant, ce qui entraîne dans le code suivant:
Il existe de nombreuses différences entre les deux approches, mais nous allons d'abord prendre un coup d'oeil sur la façon dont l' .Net framework interprète.
Dans .Net, vous pouvez remplacer que la méthode avec une méthode qui contient le même nombre d'arguments, mais vous ne pouvez pas remplacer avec une méthode contenant plus arguments, même s'ils sont tous optionnels (ce qui aurait pour résultat un appel haveing la même signature que la méthode de remplacement), disons par exemple que vous avez:
Vous pouvez surcharger une méthode avec des arguments par défaut avec une autre méthode sans argument, (ce qui a des conséquences désastreuses que je vais discuter dans un des moments), de sorte que le folloing code juridique:
lors de l'utilisation de la réflexion, on doit toujours fournir une valeur par défaut.
Qui sont toutes assez pour prouver que .Net a pris la deuxième mise en œuvre, de sorte que le comportement que l'OP a vu, c'est le droit, au moins selon .Net.
Des problèmes Avec le .Net Approche
Cependant, il existe de réels problèmes avec l' .Net approche.
Cohérence
Comme dans le cas des OP problème lors de la substitution de la valeur par défaut dans une méthode héritée, alors les résultats peuvent être imprévisibles
Lorsque l'origine de l'implantation de la valeur par défaut est modifiée, et depuis que les appelants n'ont pas à se recompilé, nous pourrions nous retrouver avec des valeurs par défaut qui ne sont plus valides
Casser le code
Lorsque nous avons une fonction avec des arguments par défaut et la dernière, nous avons ajouter une fonction sans arguments, tous les appels maintenant route vers la nouvelle fonction, brisant ainsi tout le code existant, sans notification ou avertissement!
Similaire va se passer, si on enlève la fonction sans arguments, tous les appels seront automatiquement l'itinéraire de la fonction avec les arguments par défaut, sans notification ou avertissement! bien que cela pourrait ne pas être l'intention du programmeur
En outre, il n'a pas à être régulière méthode d'instance, une méthode d'extension fera le même problème, depuis une extension de la méthode sans paramètres auront préséance sur une méthode d'instance avec les paramètres par défaut!
Résumé: éloignez-vous des ARGUMENTS OPTIONNELS, ET UTILISER à la PLACE des SURCHARGES (COMME L' .NET FRAMEWORK LUI-MÊME NE)