强制方法从基类调用子类新方法

Posted

技术标签:

【中文标题】强制方法从基类调用子类新方法【英文标题】:Force method from base to call a subclass new method 【发布时间】:2018-10-15 11:40:34 【问题描述】:

我的理解是,子类的行为与父类完全相同,除了附加方法或使用newoverride 关键字“重写”的方法。显然,这是不正确的。

请看下面的代码(为了便于阅读,去掉括号)

class BIG
    protected void Output(string text = "")
        Console.WriteLine("BIG - " + text);

    public void CallingOutput()
        Output("Outputting...");

class SMALL:BIG
    public new void Output(string text = "")
        Console.WriteLine("SMALL - " + text);

class Program
    static void Main(string[] args)
        SMALL foo = new SMALL();

我已经知道可以使用 new 关键字将 BIG.Output(string) 隐藏在 SMALL 中。直接调用时,效果很好。

>> foo.Output("Outputting...")
SMALL - Outputting...

这是与我的理解相冲突的地方。我认为定义class SMALL:BIG 与定义class SMALL 并从BIG 复制粘贴所有方法完全相同。总之,我认为上面的 SMALL 类相当于这样:

class SMALL
    public void Output(string text = "")
        Console.WriteLine("SMALL - " + text);

    public void CallingOutput()
        Output("Outputting...");

显然这是不正确的,因为

>> foo.CallingOutput()
BIG - Outputting...

当间接调用时,它仍然使用来自BIG 的原始Output(string)

foo.CallingOutput() 输出的正确方法是什么

"SMALL - Outputting..."

假设我无权访问BIG 并且不允许进行任何更改。而且我不想隐藏CallingOutput(),因为这基本上是重写课程。 (是的,隐藏CallingOutput() 使其工作)

我在发布之前进行了一些搜索,并在C++ 中找到了类似的问题。但是,这个问题的答案是不可能的。

    我不相信。如此强大的语言,却无法告诉编译器哪个方法是哪个。 这是一种不同的语言,因此该帖子是允许的,因为它不能被视为重复。

【问题讨论】:

在基类中声明方法virtual,在子类中声明override,然后基类将大小写,new 在设计上不能这样工作 是的,overridenew 不可互换。它们有不同的效果。通过显示您对foo 的使用可以使您的问题更清楚; foo 的类型和对象创建。 @abatishchev 如前所述,我不能对 BIG 进行更改。 那就不要继承了。它不是为继承而设计的。相反,您可以使用组合。创建您的 SMALL 类以引用 BIG 对象并根据需要使用它。不适用于所有情况,但可能会带您去您需要去的地方? 那么这是设计使然:您尝试破解 BIG(通过覆盖非虚拟内容来更改其行为)并且框架不允许您这样做(新作品仅在相同类型),因为这不是 BIG 的设计和预期行为方式,抱歉。但是请参阅@itsme86 的答案 - 这是正确的方法(但并不总是适用) 【参考方案1】:

将关键字 new 替换为 override 如下:

class SMALL:BIG
public override void Output(string text = "")
    Console.WriteLine("SMALL - " + text);

【讨论】:

我无法对BIG 进行更改,并且BIG.Output() 未标记为虚拟、抽象或覆盖,因此我无法执行此操作。 在你的基类中做 基类是什么意思? BIG 不是基类吗? 是的!看完这篇你就明白了link 我不明白。我在问题和评论中两次声明我不允许对基类进行更改。它就是这样,它没有标记为虚拟、抽象或覆盖,所以我不能使用override 关键字。【参考方案2】:

创建垫片

您不能修改或覆盖现有类中的任何内容,除非它被设计为被覆盖。这是设计使然。

话虽如此,您可以创建一个新的“shim”(传递)类,该类与原始类完全相同,但成员被声明为虚拟。这是通过使用newvirtual 修饰符来完成的。

一旦你有了 shim 类,你就可以正常地覆盖该方法。示例:

class Big

    public void Output(string text)  Console.WriteLine("Big - 0", text); 

    public void CallingOutput()
    
        Output("Outputting...");
    


class Shim : Big

    public new virtual void Output(string text)  base.Output(text); 

    public void CallingOutput()
    
        Output("Outputting...");
    

现在你有了一个 Shim 类,它允许你想要什么。所以子类化它(而不是 Big),看看会发生什么:

class Small : Shim

    public override void Output(string text)  Console.WriteLine("Small - 0", text); 

测试一下:

Shim s = new Shim();
s.CallingOutput();   //Original behavior

Shim s = new Small();
s.CallingOutput();   //Overridden behavior

输出:

Big - Outputting...
Small - Outputting...

Example on DotNetFiddle

【讨论】:

不,这不是我想要的。目标是让CallingOutput 调用SMALL 中的Output。在我的问题中,我已经实现了您在示例中的内容,甚至没有使用 Shim 类。请再检查一遍! 正如我所说,你不能拥有你想要的东西,设计。在我的示例中,您可以使用 Shim 而不是 big。它与您已经完成的不同,因为 Small 中的覆盖实际上有效。我将更新示例代码。 好吧,正如我的问题中所述,我不想“重写”CallingOutput,因为那基本上是重写BIG。是的,你是对的,如果它一开始就不是这样设计的,那么它不应该被覆盖。 我明白了。对不起,你不能得到你想要的。我建议 shim 比重写 BIG 工作量少,因为您仍然可以访问 this,仍然能够访问受保护的成员,仍然能够将实例存储在 IEnumerable<BIG> 中或将其作为参数传递BIG 应该在哪里,等等。不过你的电话。

以上是关于强制方法从基类调用子类新方法的主要内容,如果未能解决你的问题,请参考以下文章

如何从基类调用派生类方法?

从基类调用静态函数

使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?

从基类调用重写的方法

从基类向下转换时是不是可以调用派生对象的虚拟方法?

从基类调用派生类的隐藏(非虚拟)方法