簡體   English   中英

在 C# 中從基類調用被覆蓋的方法

[英]Calling the overridden method from the base class in 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.
    }
}

我想調用實際上在 A 或 B 中找到的MyMethod() ,假設傳入的對象實際上是 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();
        }
    }
}

我該怎么做?

調用哪個方法是通過傳遞到另一個對象構造函數的類型的多態性來確定的:

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

對不起,你完全錯了; 這將違背虛擬方法的全部意義。 如果someObjectA則將調用A.MyMethod 如果someObjectBB.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(虛擬方法)

在虛擬方法調用中,發生該調用的實例的運行時類型決定了要調用的實際方法實現。

(原文為粗體和斜體。)

准確地說,當在編譯時類型C和運行時類型R (其中RC或從C派生的類)的實例上使用參數列表A調用名為N的方法時,調用是處理如下:

• 首先,重載決議應用於CNA ,以從C聲明並由C繼承的方法集中選擇特定方法M 這在 §7.5.5.1 中有描述。

• 然后,如果M是非虛擬方法,則調用M

否則, M是一個虛方法,並且調用M相對於R 的最派生實現。

(粗體不是原文。)

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

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

•如果R包含的引入虛擬聲明M ,則這是最派生實現M

•否則,如果R包含一個覆蓋M ,那么這是最派生實現M

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

因此,在我們上面使用Cat : AnimalDog : Animal示例中,當Feeder.Feed(Animal)的參數aCat一個實例時,則Cat.Speak是派生最多的實現。 這就是為什么我們會看到“ Meow! ”而不是“ i can haz cheezburger?

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

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

因為您已將其鍵入為 BaseClass 而不是 A 或 B,所以基類是方法調用的起點。

您可以嘗試使用泛型:

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

我不確定這在構造函數中的效果如何,但您可以將其移至不同的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM