C#如何调用基类的基类中的方法?

3
假设类A继承自类B,而类B又继承自类C。现在假设在A中定义了一个虚方法void f(),同时B和C都重写了该方法。在A的任何方法中调用B.f()只需使用base.f()即可完成。那么,在A的方法中如何调用C.f()呢?

4
可能重复:https://dev59.com/2XNA5IYBdhLWcg3wVcJx - itowlson
我猜你是指从C方法调用A.f()? - Thomas Levesque
我只是猜测,但你不能将this强制转换为C吗?((C)this).f(); - Michael Stum
9个回答

11

通常情况下,通过覆盖一个类(overriding a class),您接受它提供的接口和一些所需的行为。A 不应该知道它的超级父类(super-superclass)的存在,除非在它的父类中以某种方式被显式地暴露。这实际上是可取的,因为当您子类化 B 时,您不需要知道 C 的实现方式。您甚至不应该知道 C 的存在。

如果 b 的 f() 调用了 base.f(),那么 a 的 base.f() 只需要调用自己的 base.f() 就完成了。如果没有,那么你自己唯一的方法就是通过基于反射的侵入式方法来实现。如果您发现自己需要这样做,则可能在设计上出现了错误(例如违反 Demeter 定律等)。


谢谢,这不是我自己的设计。在这种情况下,类B和C是Telerik组件。类B提供了我需要的大部分功能,但为了实现一些功能,我需要调用C中的方法。 - sisis

3
您可以通过反射或使用IL代码生成来实现。除此之外,您不能这样做,而且您真的不应该这样做,因为这听起来不像是一个好的设计。
当您在OOP中覆盖一个方法时,您基本上是在说“对于这个类型,这个方法将替换旧方法”,并提供了这样一个规定:如果必要(通常是这样),您可以调用被替换(覆盖)的方法。
然而,您不能直接跳过一个层级,除非您采用反射或IL技巧,而这也有一个很好的原因。如果中间类允许您在此方法调用中跳过它,那么您应该向其中添加一个特殊方法以供使用。之所以这样做“困难”,是因为您通常不应该这样做,您应该始终转到上一级,以便它保持控制。

1

我猜您的意思是从C中的一个方法调用A.f()?

简短回答:您不能这样做

不过,如果您控制B,您可以添加一个方法到B中来显式地调用它的基类:

protected void BaseF()
{
    base.f();
}

然后从 C 中调用该方法


1

这可以通过反射来实现。您可以获取当前类的类型,使用它来获取父类型,然后获取父类型的父类型。

之所以语言本身不直接支持此功能,是因为它破坏了继承的抽象模型。它与传统的面向对象设计并不兼容。


0

试试这个:

C c = this;
c.f();

这将打印出"C":

public static void Main()
{
 A o = new A();
 Console.WriteLine(o.f());
}

class C
{
 public virtual string f()
 {
  return "C";
 }
}

class B : C
{
 public virtual string f()
 {
  return "B";
 }
}

class A : B
{
 public virtual string f()
 {
     C c = this;
     return c.f();
 }
}

这似乎无法编译(无论我如何尝试)。你是不是在想C ++,它确实有类似的语法来调用继承链上更高层次的方法? - itowlson
这段代码无法编译,理论上也不可行。你不能像那样调用非静态方法,除非它们被声明为静态的,然而一旦被声明为静态方法,它们就不可能是虚方法了。 - Lasse V. Karlsen
抱歉,我在刚才犯了一个短暂的 C++ 错误。已经通过一种解决方法进行了编辑。 - Jason

0
从C函数中,你可以调用base.f(),这是你应该做的。如果B.f()(实际上你正在调用的)选择也调用base.f(),那么就实现了你想要的效果,但是否发生这种情况完全取决于B的实现。
你可以尝试将C强制转换为A并调用f(),像这样:
public class C : B {
    public override f() {
        ((A) this).f();
    }
}

但是这样做的问题在于C不知道f()在哪里声明 - 它只知道B中有一个可以被覆盖的实现,C不知道A的存在。上面的示例实际上是一段糟糕的代码,如果A没有实现f(),它将产生编译时错误。

0

使用new而不是override是一种方法(函数不需要是虚拟的即可使用new,尽管它可以是)。

但这可能会带来其他问题。如果在任何地方将MyClass向上转换为MyBaseClass,它将使用MyBaseClass版本的函数。

private void Form1_Load(object sender, EventArgs e)
{

    var mc = new MyClass();

    mc.Foo();
    ((MyBaseClass) mc).Foo();
}


public class MyBaseClass
{           
    public virtual void Foo ()
    {
        Console.WriteLine("Calling from MyBaseClass");
    }
}

public class MyClass : MyBaseClass
{

    new public void Foo ()
    {
        Console.WriteLine("Calling from MyClass");
    }
}

你所得到的输出为:

Calling from MyClass
Calling from MyBaseClass

0
正如早前所评论的,虚方法不能以这种方式被覆盖,必须使用“new”而非“override”。
以下是使用您的类名的示例:
这不是一个好的做法,因为它会变得极其混乱,并且违反了大部分预期的继承行为,“new”关键字旨在将新接口项强制加入原本不打算被覆盖的类中。
祝好, Jason
    class Program
{
    static void Main(string[] args)
    {
        C c = new C();
        A a = new A();
        a.DoThis();
        Console.ReadKey();
    }
}

public class C
{
    public virtual void DoThis() {
        Console.WriteLine("In C"); }
}

public class B : C
{
    public new void DoThis()
    {
        Console.WriteLine("In B");
    }
}

public class A : B
{
    public new void DoThis()
    {
        Console.WriteLine("In A..");
        ((C) this).DoThis();
    }
}

0

这似乎是一个设计问题。

你有几个选择:

  1. 将大部分 A.DoThis() 移动到另一个方法(protected?)中,命名为适当的名称(DoThat()?)。现在,你可以直接调用它。
  2. 拆分你的对象,并考虑使用组合而不是继承来重用行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接