2010-02-19 2 views
17

Dato il seguente C definizioni # classe e codice:chiamata al metodo sovrascritto dalla classe base in 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. 
    } 
} 

Vorrei chiamare il MyMethod(), che è effettivamente trovato in A o B, assumendo l'oggetto passato è in realtà un'istanza di A o B, non quella che si trova in BaseClass. A titolo di esempio:


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

Come posso fare?

+2

Questo è un odore di codice. – Will

+10

Qual è il tipo di oggetto ACTUAL che si sta passando al costruttore AnotherObject? In altre parole, qual è la nuova affermazione? Perché quello che stai descrivendo può succedere solo se hai una BaseClass nuova invece di una A o una B. – Nick

+1

Penso che il fatto che sia un odore di codice sia ovvio per David altrimenti la domanda non sarebbe stata postata e il codice puzzolente avrebbe è stato usato invece. – Greg

risposta

14

Quale metodo viene chiamato è determinata tramite il polimorfismo del tipo che viene passato al costruttore 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

Utilizzare @base. Non mi piace digitare più caratteri. –

1

Se someObject è passato in classe A, viene chiamato A.MyMethod, non l'implementazione della classe base. Guarda anche la parola chiave is.

1

Perché hai digitato come BaseClass invece di un A o un B, il baseclass è il punto di partenza per le chiamate al metodo.

Si potrebbe provare a utilizzare un generico:

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

Non sono sicuro di quanto bene questo volerà nel costruttore, ma si potrebbe essere in grado di muoversi a un metodo diverso.

9

Siamo spiacenti, ma si sbaglia completamente; questo andrebbe contro l'intero punto dei metodi virtuali. Se someObject è un A, verrà invocato il numero A.MyMethod. Se someObject è un B, verrà richiamato lo B.MyMethod. Se someObject è un BaseClass e non un'istanza di un tipo derivata da BaseClass, verrà richiamato lo BaseClass.MyMethod.

Usiamo esempio preferito da tutti:

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

Poi:

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

questo stampa:

i can haz cheezburger? 
Meow! 
Woof! 

Ancora una volta, questo è l'intero punto di metodi virtuali.

Inoltre, possiamo andare alle specifiche. Da 10.6.3 (metodi virtuali)

In una chiamata di metodo virtuale, il tipo run-time dell'istanza per cui tale invocazione avviene determina l'effettiva attuazione metodo da chiamare.

(grassetto e corsivo nell'originale.)

In termini precisi, quando un metodo denominato N viene richiamato con una lista di argomenti A in un'istanza con un tipo in fase di compilazione C e un tipo run-time R (dove R è o C o una classe derivata da C), l'invocazione viene elaborato come segue:

• Innanzitutto, risoluzione di sovraccarico viene applicato a C, N e A, selezionare un metodo specifico M dal set di metodi dichiarati e ereditato da C. Questo è descritto in § 7.5.5.1.

• Quindi, se M è un metodo non virtuale, viene richiamato M.

Altrimenti, M è un metodo virtuale e viene richiamata l'implementazione più derivata di M rispetto a R.

(Bolding non in originale.)

Poi, abbiamo bisogno della definizione di "implementazione con massima derivazione di M." Questa è una bella definizione ricorsiva:

L'implementazione con massima derivazione di un metodo virtuale M rispetto ad una classe R è determinato come segue:

• Se R contiene la dichiarazione virtuale introduzione di M, allora questo è l'implementazione più derivata di M.

• In caso contrario, se R contiene un override di M, questa è l'implementazione più derivata di M.

• Altrimenti, l'implementazione più derivati ​​M rispetto R è lo stesso come l'implementazione con massima derivazione di M rispetto alla classe base diretta di R.

Pertanto, nell'esempio di cui sopra con Cat : Animal e Dog : Animal, quando il parametro a a Feeder.Feed(Animal) è un'istanza di Cat poi Cat.Speak è l'implementazione con massima derivazione. Questo è il motivo per cui vedremo "Meow!" e non "i can haz cheezburger?"

+0

Devo aver perso la lezione in cui era coinvolto il Feeder. Con me è molto probabile. –

+0

@Yuriy Faktorovich: 'Feeder' sta semplicemente interpretando il ruolo di' AnotherObject' dell'OP. – jason

+0

@Downvoter: Devi solo dare una spiegazione qui. – jason

2

Se MyMethod() è astratto nella classe base, verrà utilizzata la versione nelle classi derivate. Quindi, se non hai bisogno di chiamare l'istanza nella classe base, questa sarebbe un'opzione.

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 dovrebbe considerare un progetto in cui BaseClass è astratto se il comportamento elencato non è desiderato. – Greg