2010-02-19 10 views
17

folgende C# Klassendefinitionen und Code Gegeben:das überschriebene Methode von der Basisklasse in C# Aufruf


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. 
    } 
} 

Ich möchte das MyMethod() aufrufen, die tatsächlich in A oder B gefunden wird, unter der Annahme, das Objekt übergeben ist eigentlich eine Instanz von A oder B, nicht die, die in BaseClass gefunden wird. Kurz etwas zu tun:


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(); 
     } 
    } 
} 

Wie kann ich es tun?

+2

Das ist ein Code-Geruch. – Will

+10

Was ist der Typ des ACTUAL-Objekts, das Sie an den AnotherObject-Konstruktor übergeben? Mit anderen Worten, was ist die neue Aussage? Weil das, was du beschreibst, nur passieren kann, wenn du eine BaseClass statt eines A oder eines B machst. – Nick

+1

Ich denke die Tatsache, dass es ein Code-Geruch ist, ist offensichtlich für David, sonst wäre die Frage nicht gepostet worden und der stinkende Code hätte wurde stattdessen verwendet. – Greg

Antwort

14

Welche Methode aufgerufen wird, wird über Polymorphismus von der Art bestimmt, die in den another Konstruktor übergeben wird:

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

Verwenden Sie @base. Ich mag es nicht, mehr Zeichen einzugeben. –

1

Wenn ein übergebenes SomeObject Klasse A ist, wird A.MyMethod aufgerufen, nicht die Basisklassenimplementierung. Sehen Sie sich auch das Stichwort is an.

1

Weil Sie es als ein Baseclass statt ein A oder ein B eingegeben haben, die Basisklasse ist der Anfangspunkt für die Methodenaufrufe.

Sie könnten versuchen, eine generische Verwendung:

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

Ich bin nicht sicher, wie gut diese im Konstruktor fliegen, aber man könnte dies zu einer anderen Methode bewegen können.

9

Sorry, aber Sie sind völlig falsch; das würde gegen den ganzen Punkt der virtuellen Methoden gehen. Wenn someObject ein A ist, wird A.MyMethod aufgerufen. Wenn someObject ein B ist, wird B.MyMethod aufgerufen. Wenn someObject eine BaseClass und keine Instanz eines von BaseClass abgeleiteten Typs ist, wird BaseClass.MyMethod aufgerufen.

Lassen Sie sich jeden Liebling Beispiel verwenden:

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!"); } 
} 

Dann:

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); 

Dieser Druck wird:

i can haz cheezburger? 
Meow! 
Woof! 

Auch dies ist der wesentliche Punkt der virtuellen Methoden.

Weiter können wir zu der Spezifikation gehen. Von 10.6.3 (Virtual Methoden)

In einem virtuellen Methodenaufruf, der Laufzeittyp der Instanz, für die dieser Aufruf erfolgt die eigentliche Implementierung der Methode aufzurufen bestimmt.

(Fettdruck und Kursivschrift im Original.)

in präziser ausgedrückt, wenn eine Methode namens N mit einer Argumentliste A auf einer Instanz mit einem Kompilierzeit-Typ C und einem Laufzeittyp R aufgerufen wird (wobei R entweder C oder eine Klasse abgeleitete von C), wird der Aufruf wie folgt verarbeitet:

• Zuerst wird die Überladungsauflösung zu C angewendet, N und A, eine spezifische Methode M aus der Menge von Methoden und vererbte durcherklärt auszuwählen 210. Dies wird in §7.5.5.1 beschrieben.

• Wenn M eine nicht virtuelle Methode ist, wird M aufgerufen.

Ansonsten ist M eine virtuelle Methode, und die am weitesten abgeleitete Implementierung von M in Bezug auf R wird aufgerufen.

(Bolding nicht in Original.)

Dann müssen wir die Definition von "most abgeleiteten Implementierung von M." Dies ist eine nette rekursive Definition:

Die abgeleitete Implementierung eines virtuellen Methode M in Bezug auf eine Klasse R wird wie folgt bestimmt:

• Wenn R enthält die Einführung von virtuellen Erklärung M, dann ist diese ist die am weitesten abgeleitete Implementierung von M.

• Andernfalls, wenn R eine Überschreibung von M enthält, ist dies die am weitesten abgeleitete Implementierung von M.

• Andernfalls die abgeleitete Umsetzung M bezüglich R ist die gleiche wie die meisten abgeleiteten Umsetzung M in Bezug auf die direkte Basisklasse R.

So wird in unserem obigen Beispiel mit Cat : AnimalDog : Animal und, wenn der Parameter a zu Feeder.Feed(Animal) ist eine Instanz CatCat.Speak dann wird die Umsetzung abgeleitet. Aus diesem Grund sehen wir "Meow!" und nicht "i can haz cheezburger?"

+0

Ich muss die Klasse verpasst haben, an der der Feeder beteiligt war. Bei mir ist es sehr wahrscheinlich. –

+0

@Yuriy Faktorovich: 'Feeder' spielt nur die Rolle des OP's' AnotherObject'. – jason

+0

@Downvoter: Sie müssen nur eine Erklärung hier geben. – jason

2

Wenn MyMethod() auf der Basisklasse abstrakt ist, wird die Version in den abgeleiteten Klassen verwendet. Wenn Sie also die Instanz nicht in der Basisklasse aufrufen müssen, wäre dies eine 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 sollte ein Design berücksichtigen, bei dem BaseClass abstrakt ist, wenn das aufgelistete Verhalten nicht erwünscht ist. – Greg

Verwandte Themen