12
votes

Pourquoi cela fonctionne-t-il? Surcharge de la méthode + Méthode Remplacement + polymorphisme

Dans le code suivant:

public abstract class MyClass
{
public abstract bool MyMethod(
        Database database,
        AssetDetails asset,
        ref string errorMessage);
}

public sealed class MySubClass : MyClass
{
    public override bool MyMethod(
        Database database,
        AssetDetails asset,
        ref string errorMessage)
    {
        return MyMethod(database, asset, ref errorMessage);
    }

    public bool MyMethod(
        Database database,
        AssetBase asset,
        ref string errorMessage)
    {
    // work is done here
}
}


6 commentaires

Avez-vous oublié un `: myClass` sur votre mysubclass de la définition?


1 - Voulez-vous vraiment dire que mysubclass comme classe intérieure? Pourquoi? 2 - Pourriez-vous donner un exemple d'invoquer la méthode pour donner le comportement que vous décrivez


MySubclass n'est pas une classe intérieure, non. Un exemple serait effectivement mysubclassinstance.mymethod (base de données, nouveaux assetDétails (), ref. Msg); - Cela frappe la première méthode, puis est transmise à la seconde


Comment "mysubclassinstance" est-il déclaré? être de la base ou du type descendant? (Indépendamment du type que vous avez mis dedans)


MySubClassinstance a été déclaré comme le type descendant.


Kasey, avez-vous dirigé ce programme? Parce que quand je l'ai fait, je n'ai jamais appellé la première méthode de mysubclass si vous passez des attestes d'assurance tout en invoquant MySubclass. Il appelle plutôt une deuxième méthode sur cet appel (méthode finale de MySubclass)


4 Réponses :


10
votes

c # résoudra votre appel à votre autre implémentation car les appels à une méthode sur un objet, où la classe pour cet objet a sa propre implémentation sera favorisée sur une nommation directe ou héritée.

Ceci peut conduire à subtil et à Des problèmes difficiles à trouver, comme vous l'avez montré ici. P>

Par exemple, essayez ce code (le lisez d'abord, puis compilez et exécutez-le), voyez si cela fait ce que vous attendez. p> xxx pré>

Notez que si vous déclarez le type de variable à être de type base code> au lieu de descendant code>, l'appel ira à l'autre méthode, essayez de changer cette ligne: p> xxx pré>

à ceci et réexécuter: p> xxx pré>

Alors, comment réussiriez-vous à appeler descendant.test (chaîne) code> alors? P>

Ma première tentative ressemble à ceci: p>

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();


8 commentaires

Comme il n'ya aucun moyen d'entrer dans ce gâchis sans avoir une méthode virtuelle et une méthode non virtuelle (vous ne pouvez pas avoir deux méthodes identiques dans la même classe) ou avoir une méthode dans la classe de base appelle une méthode dans une classe descendante Sans avoir une méthode virtuelle, je ne pense pas que ces facteurs y soient. Je ne suis toujours pas sûr de savoir pourquoi cela compte comme vous le dites, il y a beaucoup de problèmes subtils, pas tous liés à des méthodes virtuelles. Mais s'il vous plaît éclairer, et je changerai ma réponse en conséquence, je n'ai montré que ce problème par Jon Skeet, tout moment maintenant il va se réveiller et nous prouver tous tout faux ...


Comme je l'ai compris, il a pour but de remplacer la méthode de la classe de base et de la mettre en œuvre séparément, deux méthodes "identiques" dans sa classe descendante et se demandent pourquoi le compilateur a choisi un et non l'autre pour son appel.


@Lasse, oui, je vois que maintenant, vous avez exactement raison. - Désolé j'ai supprimé mon commentaire juste avant de répondre.


Ne t'inquiète pas, cette partie du compilateur est l'allée sombre que je n'ose pas vous aventurer de toute façon, car la peur d'être agressée :)


Merci beaucoup, j'ai pu prédire ce que votre application produirait grâce à votre explication, et c'était le contraire de ce que j'aurais attendu auparavant, alors je suppose que cela signifie que vous avez répondu à ma question! C'est un soulagement de savoir qu'il y a enfin une bonne raison pour cela.


@Lasse, la chose intrigante cherche qui ((quels types) a accès aux deux implémentations. Les objets de type mysubclass ont accès uniquement à la méthode non virtuelle. Objets qui dérivent de MySubclass (s'il n'était pas scellé), aurait seulement accès à la méthode virtuelle si elles étaient déclarées comme leur type de béton et n'auraient accès à la méthode non virtuelle que si elles étaient déclarées comme mysubclass. Étant donné qu'il est scellé et sa base est absttyract, dans son exemple. Il ne devrait y avoir aucun moyen d'accéder au membre virtuel du tout, non?


Eh bien, vous pouvez dans certains domaines, laissez-vous remplir ma réponse.


Et avec mes changements, vous devrez également avoir recours à une fonte, donc c'est un peu confirmé.



1
votes

Parce que c'est la façon dont la langue est définie. Pour Membres virtuels , la mise en œuvre qui est appelée à l'exécution, lorsqu'une méthode existe à la fois dans une classe de base et une classe dérivée, est basée sur le type < / Fort> de l'objet que la méthode est appelée contre, pas le type déclaré de la variable qui détient la référence à l'objet. Votre premier MyMethod est dans une classe abstraite. Donc, il peut jamais être appelé à partir d'un objet de type myClass - car aucun objet de ce type ne peut exister. Tout ce que vous pouvez instantiouver est la classe dérivée mysubclass . Le type de béton est mysubclass , donc cette mise en œuvre est appelé, peu importe le code qui l'appelle est dans la classe de base.

Pour les membres / méthodes non virtuels, l'inverse est vrai.


2 commentaires

Désolé, de clarifier: par le "premier" MyMethod, je voulais dire la première mise en œuvre, c'est-à-dire le premier mymethod dans MySubclass, mais la deuxième instanciation lexicale. Ce qui me déroure, c'est pourquoi l'occurrence finale de MyMethod est appelée lors de la réussite d'un attesse.


@Kasey, oui, vous êtes correct en ce que j'ai mal compris YR question ... La réponse de Lasse ci-dessus est juste sur la cible ...



5
votes

Voir la section de la spécification de langue C # sur Membre Recherche et Résolution de surcharge . La méthode de remplacement de la classe dérivée n'est pas un candidat en raison des règles de la recherche des membres et de la méthode de la classe de base n'est pas la meilleure correspondance en fonction des règles de résolution de la surcharge.

Section 7.3

Premièrement, l'ensemble de tous les membres accessibles (section 3.5) nommés N déclarés N et les types de base (section 7.3.1) de T sont construits. Les déclarations qui incluent un modificateur de remplacement sont exclues de l'ensemble. Si aucun membre nommé N existe et est accessible, la recherche ne produit aucune correspondance et les étapes suivantes ne sont pas évaluées.

Section 7.4.2:

Chacun de ces contextes définit l'ensemble des membres de la fonction candidat et la liste des arguments à sa manière unique, comme décrit en détail dans les sections énumérées ci-dessus. Par exemple, l'ensemble des candidats à une méthode Invocation n'inclut pas les méthodes marquées de remplacement (section 7.3) et les méthodes d'une classe de base ne sont pas candidates si une méthode d'une classe dérivée est applicable (section 7.5 .5.1). (l'emphase mine)


1 commentaires

C'est la meilleure réponse technique à cette question.



4
votes

Comme d'autres ont correctement noté, lorsque vous avez le choix entre deux méthodes de candidat applicables dans une classe, le compilateur toujours choisit celui qui était déclaré à l'origine "plus proche" à la classe qui contient le site d'appel lors de l'examen de la hiérarchie de la classe de base.

Cela semble contre-intuitif. Sûrement s'il y a une correspondance exacte déclarée sur une classe de base, il s'agit d'un meilleur match que d'une correspondance inexacte déclarée sur une classe dérivée, oui?

non. Il y a deux raisons de choisir la méthode plus dérivée toujours sur la méthode moins dérivée.

Le premier est que l'auteur de la classe dérivée a beaucoup plus d'informations que l'auteur de la classe de base. L'auteur de la classe dérivée connaît tout sur la classe de base et la classe dérivée, qui est, après tout, la classe que l'appelant utilise réellement. Lorsque vous avez le choix entre appeler une méthode écrite par une personne qui connaît tout ce qui connaît une personne qui ne connaît que quelque chose sur le type que l'appelant utilise, il est clairement logique de hiérarchiser la méthode écrite par le concepteur de la classe dérivée.

Deuxièmement, faire ce choix entraîne une forme de la défaillance de la classe de base fragile. Nous souhaitons vous protéger de cette défaillance et nous avons donc écrit les règles de résolution de la surcharge afin de l'éviter dans la mesure du possible.

Pour une explication détaillée de la manière dont cette règle vous protège de l'échec de la classe de base fragile, voir mon article sur le sujet:

http: / /Blogs.msdn.com/ericlippert/archive/2007/09/04/futureoPreaking-changes-par-three.aspx

et pour les articles d'une autre manière que les langues traitent des situations de classe de base fragiles, voir:

http://blogs.msdn.com/ B / ERICLIPPERT / Archive / Tags / BRITING + BASE + CLASSES /


1 commentaires

Merci; C'est très intéressant de voir la justification de la décision.