【问题标题】:How to override an inherited method from within another inherited class如何覆盖另一个继承类中的继承方法
【发布时间】:2021-11-30 13:02:33
【问题描述】:

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

例子:

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

继承基类的类。我想如果我再次添加该方法并将其标记为 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 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.");
    }
}

运行结果是这样的:

这是覆盖 DoMath 中的基础
这是 LogThings 3 中的基础

有没有办法做到这一点?

【问题讨论】:

  • 您为什么声称DoMath 中的方法在未覆盖基本方法时会覆盖它,而是创建一个新方法?如果你想覆盖它实际上是override
  • 您正在创建DoMath 的实例,而不是Program 的实例。但即使您要创建 Program 的实例,DoMath 也会重新定义方法 WriteLog 而不是覆盖它。
  • 我想如果我再次添加该方法并将其标记为 new 以实现为虚拟 - 再次由我运行?
  • 非常困惑您想要什么以及您如何努力实现这一目标...stackoverflow.com/questions/392721/… 可能适合您阅读edit 问题以阐明您想要实现的目标以及您需要成为什么解释。

标签: c# inheritance polymorphism


【解决方案1】:

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

  • override modifiervirtualabstract 方法相关;带有override 修饰符的方法只是提供了现有virtualabstract 方法的替代实现。 virtual 方法有一个默认实现,abstract 方法根本没有实现。
  • 不过,new modifier 可以应用于 any 方法,并且只允许重用已使用的 name。这些方法之间完全没有关系,唯一的相似之处是它们强制使用相同的名称。

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

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

那,您可能想改用var m = new Program(); - 原因如下所述。


考虑这两段代码:

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

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

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

这是 LogThings 中的一些基础

如上所述:new 切断该链接。

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

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

不出意外,这段代码会打印出来

这是覆盖 DoMath 中的基数

这是 LogThings 中的一些基础

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

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

var m = new DoMath();

最后,考虑一下这个版本:

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

这里,Program 类实际上是overridesvirtual void WriteLog 方法DoMath。因此,此代码打印

这不是压倒一切的。

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


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

您可以认为 vtable 中的条目类似于指向方法的实际实现(来自当前类的方法)的指针,然后是指向前一个实现的指针。

在您的 DoMathProgram 示例中,实例化 DoMath 类将生成一个仅包含以下内容的 vtable

DoMath.WriteLog(string) -> null

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

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

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

但是请注意其中的临时null。因为DoMath 类将WriteLog 方法声明为new,所以它被认为是一种新方法,与LogThings 中的方法无关。对于LogThings,vtable 世界仍然看起来像这样:

LogThings.WriteLog(string) -> null

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

请注意,这种对 vtable 的描述过于简单化了;然而,关于这个主题有很多很好的材料。

【讨论】:

  • 谢谢!这是对这个问题的一个很好的回顾。我认为随着对“新”关键字的深入研究以及我对它的滥用,事情在另一个分支上发生了。还是很透彻的回答。感谢您花时间写出来。
【解决方案2】:

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

“失败”的地方在这里:

var m = new DoMath();

您没有使用其重写方法创建Program 的实例,因此永远不会使用重写。请改用new Program()

【讨论】:

    猜你喜欢
    • 2012-11-20
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 2012-10-15
    • 2016-01-24
    • 1970-01-01
    相关资源
    最近更新 更多