【问题标题】:Calling the overridden method from the base class in C#从 C# 中的基类调用重写的方法
【发布时间】:2011-01-18 22:53:00
【问题描述】:

给定以下 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 构造函数的 ACTUAL 对象的类型是什么?换句话说,新的声明是什么?因为您所描述的只有在您新建 BaseClass 而不是 A 或 B 时才会发生。
  • 糟糕。才意识到我的问题。我认为重写该函数的几个类之一实际上未能重写该函数。问题解决了。
  • 我认为这是代码异味这一事实对 David 来说是显而易见的,否则问题就不会发布,而是会使用异味代码。

标签: c# inheritance methods virtual overriding


【解决方案1】:

对不起,你完全错了;这将违背虚拟方法的全部观点。如果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 开始(虚拟方法)

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

(原文为粗体和斜体。)

准确地说,当在编译时类型为C 和运行时类型为R(其中RC 或派生自 C 的类),调用处理如下:

• 首先,重载决议应用于CNA,以从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 示例中,当aFeeder.Feed(Animal) 的参数是Cat 的一个实例时,Cat.Speak 是最派生的实现。这就是为什么我们会看到“Meow!”而不是“i can haz cheezburger?

【讨论】:

  • 我一定错过了涉及 Feeder 的课程。对我来说很有可能。
  • @Yuriy Faktorovich:Feeder 只是扮演 OP 的AnotherObject 的角色。
【解决方案2】:

调用哪个方法是通过传递给 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() 

【讨论】:

  • 使用@base。我不喜欢输入更多的字符。
【解决方案3】:

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

【讨论】:

  • +1 如果不需要列出的行为,则应该考虑 BaseClass 是抽象的设计。
【解决方案4】:

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

您可以尝试使用泛型:

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

我不确定它在构造函数中的效果如何,但您可以将其移至其他方法。

【讨论】:

    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2011-06-17
      • 2011-02-20
      • 2017-02-04
      相关资源
      最近更新 更多