2010-02-19 4 views
17

Compte tenu de ce qui suit C# définitions de classe et le code:L'appel de la méthode surchargée de la classe de base en C#


public class BaseClass 
{ 
    public virtual void MyMethod() 
    { 
     ...do something... 
    } 
} 

public class A : BaseClass 
{ 
    public override void MyMethod() 
    { 
     ...do something different... 
    } 
} 

public class B : BaseClass 
{ 
    public override void MyMethod() 
    { 
     ...do something different... 
    } 
} 

public class AnotherObject 
{ 
    public AnotherObject(BaseClass someObject) 
    { 
     someObject.MyMethod(); //This calls the BaseClass method, unfortunately. 
    } 
} 

je voudrais appeler l'MyMethod() qui est effectivement trouvé dans A ou B, en supposant l'objet passé est une instance de A ou B, pas celle qui se trouve dans BaseClass. À court de faire quelque chose comme ceci:


public class AnotherObject 
{ 
    public AnotherObject(BaseClass someObject) 
    { 
     A temp1 = someObject as A; 
     if (A != null) 
     { 
      A.MyMethod(); 
     } 

     B temp2 = someObject as B; 
     if (B != null) 
     { 
      B.MyMethod(); 
     } 
    } 
} 

Comment puis-je faire?

+2

C'est une odeur de code. – Will

+10

Quel est le type de l'objet ACTUAL que vous transmettez au constructeur AnotherObject? En d'autres termes, quelle est la nouvelle déclaration? Parce que ce que vous décrivez ne peut arriver que si vous créez une BaseClass au lieu d'un A ou un B. – Nick

+1

Je pense que le fait que c'est une odeur de code est évident pour David sinon la question n'aurait pas été postée et le code malodorant aurait été été utilisé à la place. – Greg

Répondre

14

Quelle méthode est appelée est déterminée par le polymorphisme du type qui est passé dans le constructeur AnotherObject:

AnotherObject a = new AnotherObject(new A()); // invokes A.MyMethod() 
AnotherObject b = new AnotherObject(new B()); // invokes B.MyMethod() 
AnotherObject c = new AnotherObject(new BaseClass()); //invokes BaseClass.MyMethod() 
+1

Utilisez @base. Je n'aime pas taper plus de caractères. –

1

Si un objet passé est la classe A, alors A.MonMethod est appelée, pas l'implémentation de la classe de base. Regardez également le mot-clé is.

1

Parce que vous avez tapé comme un BaseClass au lieu d'un A ou un B, la classe de base est le point de départ des appels de méthode.

Vous pouvez essayer d'utiliser un générique:

public class AnotherObject 
{ 
    public AnotherObject<T>(T someObject) where T : BaseClass 
    { 
     someObject.MyMethod(); //This calls the BaseClass method, unfortunately. 
    } 
} 

Je ne sais pas comment cela va voler dans le constructeur, mais vous pourriez être en mesure de se déplacer à un autre méthode.

9

Désolé, mais vous vous trompez complètement; cela irait à l'encontre du point entier des méthodes virtuelles. Si someObject est un A, A.MyMethod sera appelé. Si someObject est B alors B.MyMethod sera invoqué. Si someObject est un BaseClass et non une instance d'un type dérivé de BaseClass alors BaseClass.MyMethod sera appelé.

Utilisons exemple préféré de tout le monde:

class Animal { 
    public virtual void Speak() { 
     Console.WriteLine("i can haz cheezburger?"); 
    } 
} 
class Feeder { 
    public void Feed(Animal animal) { animal.Speak(); } 
} 
class Cat : Animal { 
    public override void Speak() { Console.WriteLine("Meow!"); } 
} 
class Dog : Animal { 
    public override void Speak() { Console.WriteLine("Woof!"); } 
} 

Puis:

Animal a = new Animal(); 
Animal c = new Cat(); 
Animal d = new Dog(); 
Feeder f = new Feeder(); 
f.Feed(a); 
f.Feed(c); 
f.Feed(d); 

Ce imprimera:

i can haz cheezburger? 
Meow! 
Woof! 

Encore une fois, cela est tout le point de méthodes virtuelles.

En outre, nous pouvons aller à la spécification. De 10.6.3 (Méthodes virtuelles)

Dans un appel de méthode virtuelle, le type run-time de l'instance pour laquelle cette invocation a lieu détermine la mise en œuvre de la méthode actuelle d'invoquer.

(En gras et en italique dans l'original.)

En termes précis, lorsqu'une méthode nommée N est invoqué avec une liste d'arguments A sur une instance avec un type de compilation C et un type d'exécution R (où R est soit C ou une classe dérivée de C), l'appel est traité comme suit:

• Premièrement, la résolution de surcharge est appliqué à C, N et A, pour sélectionner une méthode M spécifique de l'ensemble des méthodes déclarées dans et héritée par C Ceci est décrit au § 7.5.5.1.

• Ensuite, si M est une méthode non virtuelle, M est appelée.

Sinon, M est une méthode virtuelle, et la mise en œuvre la plus dérivée de M par rapport à R est invoquée.

(Bolding pas dans l'original.)

, nous avons besoin ensuite la définition de "la mise en œuvre la plus dérivée de M." Cette définition est récursive agréable:

La mise en œuvre la plus dérivée d'une méthode virtuelle M par rapport à une classe R est déterminée comme suit:

• Si R contient la déclaration virtuelle introduction de M, cette est l'implémentation la plus dérivée de M.

• Sinon, si R contient un remplacement de M, il s'agit de l'implémentation la plus dérivée de M.

• Dans le cas contraire, la mise en œuvre la plus dérivée de M par rapport à R est la même que la mise en œuvre la plus dérivée de M par rapport à la classe de base directe de R.

Ainsi, dans notre exemple ci-dessus avec Cat : Animal et Dog : Animal, lorsque le paramètre a-Feeder.Feed(Animal) est une instance de Cat alors Cat.Speak est la mise en œuvre la plus dérivée. C'est pourquoi nous verrons "Meow!" et non "i can haz cheezburger?"

+0

J'ai dû raté la classe où le Feeder était impliqué. Avec moi c'est très probable. –

+0

@Yuriy Faktorovich: 'Feeder' joue juste le rôle du' AnotherObject' de l'OP. – jason

+0

@Downvoter: Il suffit de donner une explication ici. – jason

2

Si MyMethod() est abstrait sur la classe de base, la version dans les classes dérivées sera utilisée. Donc, si vous n'avez pas besoin d'appeler l'instance dans la classe de base, ce serait une option.

static void Main(string[] args) 
    { 

     A classA = new A(); 
     B classB = new B(); 

     DoFunctionInClass(classA); 
     DoFunctionInClass(classB); 
     DoFunctionInClass(classA as BaseClass); 

     Console.ReadKey(); 
    } 

    public static void DoFunctionInClass(BaseClass c) 
    { 
     c.MyMethod(); 
    } 



public abstract class BaseClass 
{ 
    public abstract void MyMethod(); 
} 


public class A : BaseClass 
{ 
    public override void MyMethod() 
    { 
     Console.WriteLine("Class A"); 
    } 
} 

public class B : BaseClass 
{ 
    public override void MyMethod() 
    { 
     Console.WriteLine("Class B"); 
    } 
} 
+0

+1 doit considérer un design où BaseClass est abstrait si le comportement indiqué n'est pas souhaité. – Greg

Questions connexes