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

时间:2010-02-19 20:36:18

标签: c# inheritance methods virtual override

给出以下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();
        }
    }
}

我该怎么做?

5 个答案:

答案 0 :(得分: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 :(得分: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(虚拟方法)

  

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

(Bolding and italics in original。)

  

准确地说,在具有编译时类型N和运行时类型{{1}的实例上使用参数列表A调用名为C的方法时(RR或从C派生的类),调用按如下方式处理:

     

•首先,重载决策适用于CCN,以便从声明并继承的方法集中选择特定方法AM。这在§7.5.5.1中有描述。

     

•然后,如果C是非虚方法,则会调用M

     

否则,M是一个虚方法,并且调用了与{R}相关的M的派生最多的实现。

(Bolding不是原版。)

然后,我们需要定义“M的大多数派生实现”。这是一个很好的递归定义:

  

关于类M的虚拟方法M的最派生实现确定如下:

     

•如果R包含R的引入虚拟声明,那么这是M的派生程度最高的实现。

     

•否则,如果M包含R的覆盖,那么这是M的派生程度最高的实现。

     

•否则,M相对于M的派生最多的实现与R相对于M的直接基类的最多派生实现相同}。

因此,在我们上面的RCat : Animal示例中,当参数Dog : AnimalaFeeder.Feed(Animal)的实例时,{{1}是派生最多的实现。这就是为什么我们会看到“Cat”而不是“Cat.Speak

答案 2 :(得分: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");
    }
}

答案 3 :(得分:1)

如果传入的someObject是A类,则调用A.MyMethod,而不是基类实现。另请查看is关键字。

答案 4 :(得分:1)

因为您已将其键入为BaseClass而不是A或B,所以基类是方法调用的起始点。

您可以尝试使用通用:

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

我不确定它在构造函数中的效果如何,但您可以将其移动到另一种方法。