Polymorphisme: accéder à sois-même

Signaler
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
-
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
-
En PHP et en C++, il est possible de choisir à quelle classe on veut parler directement lors d'un polymorphisme

this. remonte à l'enfant
parent:: remonte au parent
NameOfYourClass:: appelle directement la classe en question

Alors voici l'exemple
j'ai

A -> B -> C
A étant une classe mère abstraite
B étant la fille de A et abstratite
C étant la fille de B et NON abstraite

Chacun implémente la méthode "calculate()" avec "virtual" et "override"
J'instancie C

dans une fonction de B j'appelle ceci :

private void something() {
    this.calculate(); // Will call C
    base.calculate(); // Will call A
    // Comment appeler B.calculate() ?
}

Comme je disais, en PHP je ferais : B::calculate(), même chose en C++... Alors en C# ?

J'ai joué avec les virtual, override et new keyword pour les méthodes, mais je ne veux pas perdre mon principe de polymorphisme avec mon this, je veux seulement appeler ma méthode sur B directement dans ce cas ci.

Merci

12 réponses

Messages postés
6814
Date d'inscription
dimanche 15 décembre 2002
Statut
Modérateur
Dernière intervention
13 octobre 2010
22
[Je redirige vers csharpfr.com tu auras surement plus de retour]

Conceptuellement parlant je trouve ca "choquant" de faire un A::calculate() dans C, dans ce cas l'héritage n'a pas vraiment de sens puisqu'il y aurait un saut d'héritage.

Dans ton exemple base.calculate() n'est pas équivalent à A::calculate() mais plutot à B::calculate(), le mot clé base se refere à la classe parente et pas la classe orginal (sauf si évidement la méthode calculate n'est pas surchargé dans B ce qui n'est pas le cas).

En tout cas, je ne connais pas de façon de faire ce que tu demandes en C# (à part utiliser la reflection ce qui est beurk). Mais je ne pense pas que ca manque en C#, au contraire même ...

<hr />Cyril - MSP - MCPD ASP.net & MCTS SQL - Consultant indépendant
Messages postés
339
Date d'inscription
dimanche 26 janvier 2003
Statut
Membre
Dernière intervention
25 mars 2009
4
Si ce sont des méthodes statiques tu peux faire
A.Calculate()
B.Calculate()
de partout, mais bon, essaye plutot de repenser ton code.
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
1
Voici un morceau de code
Si vous ne savez pas, ne répondez pas "pense ton code différemment". Je sais très bien que si ne ne trouve pas de solution, je vais prendre cette option.

// A
public virtual int calculate(){
 int val = 10;
 return val;
}

protected abstract void doSomething();

// B
public override int calculate() {
 int val = base.calculate();
 val += 2;
 return val;
}

protected override void doSomething() {
 // ICI
 $valTotal = $this.calculate(); // 5+2+10
 // Je souhaite avoir seulement 12 (10+2)
 // $valHere = B.calculate();
 // pour finalement faire
 // $valTotal - $valHere => 5
}

// C
public override int calculate() {
 int val = base.calculate();
 val += 5;
 return val;
}

public childMethod() {
 doSomething();
}

C c = new C();
c.childMethod();

Regardez la méthode
B.doSomething()

, EssayezTestez avant de Poser une question !
Messages postés
6814
Date d'inscription
dimanche 15 décembre 2002
Statut
Modérateur
Dernière intervention
13 octobre 2010
22
Inutile d'être agressif !

Ce n'est pas possible en C# est je pense une réponse tout à fait acceptable et qui permet de te faire avancer. Comme je n'ai pas la prétention de tout savoir sur tout, je ne voulais pas être catégorique.

Je comprend parfaitement ce que tu demandes (c'est d'ailleurs rare que les questions soient aussi bien posées ;-)). Mais selon moi, conceptuellement ce n'est pas correcte.
Si tu surcharges c'est pour changer le comportement de ta classe, ce que tu voudrais c'est que l'instance de C se comporte de temps en temps comme une instance de C et parfois comme une instance de B.
On pourrait esperer que caster this en B au niveau de l'appel de calculate resoudrais le problème, mis C# ne fonctionne pas comme ça, et heureusement car je ne trouverais pas ca propre.

Cette histoire de cast me fait penser à l'implémentation explicite des interfaces, mais je ne vois pas en quoi cela pourrait t'aider (meme en rien du tout).

Mis à part la reflexion, je ne vois pas de solution, et je trouves que ce tu cherches à faire n'est pas la facon la plus élégante de faire  (conceptuellement parlant).

<hr />Cyril - MSP - MCPD ASP.net & MCTS SQL - Consultant indépendant
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
1
Je n'étais pas agressif en tant que tel. Si tu exprimes ton point, tout est OK. Si tu fais juste répondre à la réponse en disant "bad design", ça je trouve cela inutile.
---
Je vois, je ne crois pas que le fait de dire que c'est un mauvais design peut arranger les choses...
Si j'avais mis des virtual et new virtual et des choses comme ça que C# supporte. Vous auriez surement dit "Oh my god, c'est vraiment mélangeant tout ça". Alors pourquoi C# le supporte?
Il y a des méthodes différentes de programmer et certaines sont plus efficaces que d'autres je l'admet mais si un langage supporte quelque chose, c'est parce que c'est possible de le faire.

for(int i = 0; i < myArray.Length; i++) {
   if(myArray[i] == false) {
    continue;
  }
  otherArray[i] = "PASS";
}

Pourquoi l'avoir codé ainsi plutôt que d'avoir fait
for(int i = 0; i < myArray.Length; i++) {
   if(myArray[i] == true) {
     otherArray[i] = "PASS";
  }
}

Peut être que dans la logique de ma méthode, les personnes dans mon entreprise qui liront mon code comprendrons mieux le premier code que le 2e et ce sera plus facile de faire une mise à jour.
J'ai écrit tout ça pour dire que... mauvais design ou non, tout le monde ne code pas comme nous :)

Pour en revenir à nos moutons, s'il n'y a vraiment pas de keyword pour accéder à ma méthode, je vais modifier le design.
Messages postés
4936
Date d'inscription
lundi 17 février 2003
Statut
Modérateur
Dernière intervention
14 février 2014
32
clair que conceptuellement c'est inutile !

en C#, on a seulement :
- this. : instance courante
- base. : type ancestre de l'instance.

Maintenant, ce n'est pas parce que PHP le fait que c'est quelque chose de bien.
Dans ton exemple, tu sembles vouloir implémenter le pattern "Chain Of Responsability" : http://www.dofactory.com/Patterns/PatternChain.aspx

Sébastien FERRAND (blog)
Consultant Sénior
[Microsoft Visual C# MVP]
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
16
il est une solution peu propre et qui risque de te poser des problemes de polymorphisme. si B n'override pas la method mais en fait un new, alors dans ta classe C, base.Calculate appelera A.Calculate, this.Calculate appelra C.calculate, et ((B)this).calculate appelera B.Calculate.


Attention cependant, cette méthode est risquée et tu dois prendre en compte les conséquences de cette méthode sur la résoltuion des méthodes virtuelles dans le reste de ton code (si tu utilise le polymorphisme, sinon pas de soucis). Ca fonctionne, et je ne suis pas choqué outre mesure conceptuellement parlant (A part le fait que niveau maintenabilité cest pas terrible, et que ca force a se poser bcp de questions a chaque fois que tu vas utiliser tes objets et le polymorphisme).
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
16
sauf erreur de ma part (validé la réponse trop vite), car je ne suis pas entièrement sur qu'il soit possible d'override calculate dans C si B fait un new sur la méthode (je ne sais pas exactement quel choix a été fait a ce niveau, sachant que les deux possibilités sont logiques et acceptables)
Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
16
autre solution (pardon pour le poste multiple), ne rien mettre en virtual, tout en new, et a ce moment tu peux appeler en préfixant dirrectemnt (cest a dire this.calculate, A.Calculate, B.Calculate). je pense que dans ce cas base.calculate appelera B.calculate dans ce cas. cependant le polymorphisme est alors totalement impossible. et en tapant ca je me rend compte que je dis probablement une connerie dans mon premier post car le new doit probablement supprimer le virtual
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
1
Haha, merci pour tes réponses,
j'ai joué avec ce new et virtual pour essayer un compromis et ça ne fait pas ce que je veux. Il y avait un petit texte à la fin de mon premier poste disant :
"je ne veux pas perdre mon principe de polymorphisme avec mon this"

Dans B.calculate, je fais this.Calculate(), alors j'en ai besoin de mon polymorphisme.

Pour ce qui est de "Chain Of Responsability", non je ne souhaite pas faire ça.

La réponse finale à ceci, si personne d'autre ne se manifeste en faveur est que je vais simplement modifier le design parce que C# ne supporte pas de pointer directement à une classe.

Messages postés
1160
Date d'inscription
vendredi 23 juillet 2004
Statut
Membre
Dernière intervention
21 octobre 2010
16
dans B, tu dois pouvoir faire B.Calculate. Ou alors appeler la méthode calculate réelle par reflection (si ton but est d'appeler la méthode réelle de this), mais ca devient bien compliqué pour un si petit probleme de conception
Messages postés
1662
Date d'inscription
lundi 16 septembre 2002
Statut
Membre
Dernière intervention
30 juillet 2008
1
oui je sais que si je mets des new au lieu de override et que j'utilise la réflexion, je pourrais m'en sortir... Mais c'est de l'overhead pour rien.
Je vais récrire quelque chose... Quitte à duppliquer le code un peu pour que ça fonctionne... en fait c'est que je passe du code PHP vers C#. Alors le but est que ça soit compréhensible pour moi en C# puisque je vais juste fournir un DLL.

Merci à ceux qui se sont donné la peine de répondre des réponses intelligentes :)