从C#中的基类调用重写的方法

Dav*_*vid 19 c# methods virtual inheritance overriding

给出以下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.
    }
}
Run Code Online (Sandbox Code Playgroud)

我想调用实际在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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我该怎么做?

Jon*_*anK 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() 
Run Code Online (Sandbox Code Playgroud)


jas*_*son 9

对不起,但你完全错了; 这将违背虚拟方法的全部要点.如果someObject是,A那么A.MyMethod将被调用.如果someObject是,B那么B.MyMethod将被调用.如果someObject是a BaseClass而不是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!"); }
}
Run Code Online (Sandbox Code Playgroud)

然后:

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);
Run Code Online (Sandbox Code Playgroud)

这将打印:

i can haz cheezburger?
Meow!
Woof!
Run Code Online (Sandbox Code Playgroud)

同样,这是虚拟方法的全部要点.

此外,我们可以参考规范.从10.6.3(虚拟方法)

在虚方法调用中,进行该调用的实例的运行时类型决定了要调用的实际方法实现.

(原文中的粗体和斜体.)

准确地说,当在具有编译时类型和运行时类型(其中是或者派生自的类)的实例上使用N参数列表调用named方法时,将按如下方式处理调用:ACRRCC

•首先,过载分辨率就被应用于C,NA,选择一个特定的方法M从该组中声明和继承的方法C.这在§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,当参数ato Feeder.Feed(Animal)Catthen 的实例时Cat.Speak是最派生的实现.这就是为什么我们会看到Meow!"而不是" i can haz cheezburger?"