2010-02-19 89 views
17

鑑於下面的C#類定義和代碼:調用從基類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. 
    } 
} 

我想調用的MyMethod(),其實際上是在A或B中發現,假設對象傳入的實際上是A或B的實例,而不是在BaseClass中找到的實例。短缺做這樣的事情:


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

我該怎麼辦?

+2

這是一種代碼異味。 – Will

+10

您傳遞給AnotherObject構造函數的ACTUAL對象的類型是什麼?換句話說,新的陳述是什麼?因爲你所描述的只能發生在你新建一個BaseClass而不是A或B的情況下。 – Nick

+1

我認爲這是一個代碼異味的事實對於David來說是顯而易見的,否則這個問題就不會被公佈出來,而且臭的代碼會有被用來代替。 – Greg

回答

14

哪一種方法被稱爲是通過多態性上傳遞到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

使用@base。我不喜歡輸入更多字符。 –

1

如果傳入的someObject是類A,則調用A.MyMethod,而不是基類實現。另請看is關鍵字。

1

因爲你已經輸入它作爲一個BaseClass的,而不是爲A或B,基類的是方法調用的開始點。

您可以嘗試使用通用:

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

我不知道有多好,這將在構造函數中飛翔,但你也許可以將此移動到不同的方法。

9

對不起,但你完全錯了;這將違背虛擬方法的整個點。如果someObjectA那麼A.MyMethod將被調用。如果someObjectB,則將調用B.MyMethod。如果someObjectBaseClass而不是從BaseClass派生的類型的實例,則將調用BaseClass.MyMethod

讓我們用大家最喜歡的例子:

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

然後:

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

這將打印:

i can haz cheezburger? 
Meow! 
Woof! 

再次,這是虛擬方法的整個點。

此外,我們可以去說明書。從10.6.3(虛擬方法)

在虛擬方法調用中,運行時類型爲其中該調用發生確定實際方法實現調用實例的

(原始中的粗體和斜體。)

在精確的術語,當命名爲N方法被調用,有一個參數列表A上的實例與編譯時類型C和一個運行時類型R(其中R要麼C或類派生從C),如下所述調用處理:

•首先,過載分辨率就被應用於CN,和A,選擇從該組中聲明的方法的具體方法和M通過繼承210。這在§7.5.5.1中有描述。如果M是非虛擬方法,則調用M

否則,M是一個虛擬方法,並且調用了關於R的最多導出實現M

(不加粗原)

然後,我們需要定義 「最派生實現的M。」這是一個很好的遞歸定義:

一個虛擬方法M最派生實現相對於R被確定爲一類如下:

•如果R包含M導入虛擬聲明,那麼這是M的派生實現。

•否則,如果R包含M的覆蓋,那麼這是M最派生實現。

•否則,M相對於R最派生實現相同相對於直接基類的R最派生實現的M

因此,在我們上面Cat : AnimalDog : Animal,當參數aFeeder.Feed(Animal)Cat然後Cat.Speak一個實例是最衍生實現示例。這就是爲什麼我們會看到「Meow!」而不是「i can haz cheezburger?

+0

我一定錯過了Feeder參與的課程。對我來說,這很可能。 –

+0

@Yuriy Faktorovich:'Feeder'只是扮演OP的'AnotherObject'的角色。 – jason

+0

@Downvoter:你只需要在這裏解釋一下。 – jason

2

如果MyMethod()在基類上是抽象的,那麼將使用派生類中的版本。所以如果你不需要調用基類中的實例,這將是一個選項。

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應該考慮一個設計,其中BaseClass是抽象的,如果不需要列出的行爲。 – Greg

相關問題