如何从另一个继承的方法中覆盖继承的方法 class

How to override an inherited method from within another inherited class

我想知道是否可以通过调用控制台应用程序或超级超级 class 来覆盖方法?我知道我可以在 DoMath 中覆盖 WriteLog....但是考虑到我想在控制台应用程序中管理它。

示例:

public class LogThings
{
    public virtual void WriteLog(string value)
    {
        Console.WriteLine("This is the base in LogThings " + value);
    }
}

class继承基础。我有点想,如果我再次添加该方法并将其标记为 new 以作为虚拟实现,那么我可以在继承 DoMath 的控制台应用程序中覆盖它吗?

public class DoMath : LogThings
{
    public double DoAddition(double a, double b)
    {
        double result;
        result = a + b;
        WriteLog(result.ToString()); // < the operation I need to overload
        return result;
    }

    public new virtual void WriteLog(string value)
    {
        Console.WriteLine("this is overriding the base in DoMath");
        base.WriteLog(value);
    }
}

一些控制台应用程序使用 doMathANDLog class 库:

class Program : DoMath
{
    static void Main(string[] args)
    {
        var m = new DoMath();
        m.DoAddition(1, 2);
      
        Console.ReadLine();
    }

    public override void WriteLog(string value)
    {
        Console.WriteLine("this is not overriding.");
    }
}

当运行时结果是这样的:

this is overriding the base in DoMath
This is the base in LogThings 3

有办法吗?

如评论中所述,DoMath 提供了 WriteLog 方法的新实现(在定义中)。然后,您的 Program class 会覆盖该实现。

'failing' 在这里:

var m = new DoMath();

您没有使用覆盖的方法创建 Program 的实例,因此永远不会使用覆盖。请尝试 new Program()

这是一个既有趣又烦人的问题! :) 我相信这里缺少的 link 之一是:

  • override modifiervirtualabstract方法相关;带有 override 修饰符的方法只是为现有的 virtualabstract 方法提供替代实现。 virtual 方法有默认实现,abstract 方法根本没有实现。
  • 然而,new modifier 可以应用于 any 方法,并且只允许重用已经使用的 name .这些方法完全没有关系,唯一的相似之处就是强行重名了。

new 的问题是使用 new 方法的类型“假装”原始实现不存在。然而 base class 完全没有意识到这一点 - new 切断了层次结构中的 link,这就是你的问题所在。

一般来说,您不想使用 new 修饰符。

那个,您可能想改用 var m = new Program(); - 原因在下面解释。


考虑这两段代码:

LogThings a = new DoMath();
a.WriteLog("something");

LogThings a = new Program();
a.WriteLog("something");

此时调用的方法是LogThings.WriteLog()。即使我们实例化一个提供 new 方法的 DoMathProgram class,世界的 LogThings 部分并不“知道”那个。相反,它相信有一个 virtual 方法不会碰巧被覆盖。结果,此代码打印:

This is the base in LogThings something

如上所述:new 切断 link.

在下一个示例中,调用的方法确实是 DoMath.WriteLog(),因为我们现在只是实例化 DoMath() class 并调用它的 LogThings 方法。

DoMath b = new DoMath();
b.WriteLog("something");

不足为奇,此代码打印

this is overriding the base in DoMath

This is the base in LogThings something

请注意,它不会打印“这不是覆盖”,因为我们没有实例化 Program class 的实例。同样,base.LogThings() 调用与“覆盖”无关,它只是将焦点更改为 LogThings 类型并调用它知道的任何实现。

这与您使用的原始代码相似:

var m = new DoMath();

最后,考虑这个版本:

DoMath c = new Program();
c.WriteLog("something");

这里的Programclass实际上是overridesDoMathvirtual void WriteLog方法。因此,此代码打印

this is not overriding.

...现在是错误的,因为确实如此。


理解这一点的关键是每个包含虚方法或抽象方法的 class 都有所谓的虚函数 table 或 vtable。此 vtable 由派生的 classes“继承”,并允许编译器知道调用方法的哪个实现(通过所谓的虚拟分派)。

您可以将 vtable 中的条目视为指向方法实际实现的指针(来自当前 class 的那个),然后是指向以前的实现。

在您的 DoMathProgram 示例中,实例化 DoMath class 将生成仅包含

的 vtable
DoMath.WriteLog(string) -> null

而实例化 Program class 会产生这样的条目:

Program.WriteLog(string) -> DoMath.WriteLog(string) -> null

这就是 ((DoMath)new Program()).WriteLog() 起作用的原因 - 即使我们查看 DoMath 引用,编译器也会查找实例化类型的 vtable (Program)并且可以跟随链条直到实际实施 (Program.WriteLog)。

但是请注意其中的临时 null。因为 DoMath class 将 WriteLog 方法声明为 new,它被认为是一个 - 很好的 - 新方法,与 [=36= 中的方法无关].对于LogThings,vtable的世界看起来还是有点像这样:

LogThings.WriteLog(string) -> null

因为没有合法的 override - 只有一个 不同的 方法恰好具有相同的名称 - ((LogThings)new Program()).WriteLog() 调用 LogThings.WriteLog(),因为那是链中的最后一个实现。 new 本质上是“强迫”另一个 vtable,导致这种有点脑裂的设置。

请注意,对 vtable 的描述过于简单化了;然而,关于该主题有很多好的 material。