如何从基类强制调用子类的new方法?子类继承认知误区解析
new方法 我懂你现在的困惑——之前以为子类除了新增或者用new/override重写的方法外,行为和父类完全一致,但实际跑代码才发现,基类的CallingOutput还是调用自己的Output,根本没触发子类的new方法,这确实打破了之前的认知。
先给你理清楚问题根源:你用new关键字定义的子类Output,本质是隐藏了父类的同名方法,而不是重写。在编译阶段,基类里的Output调用就已经被绑定到基类自己的protected Output方法了——子类的new Output其实是个完全独立的方法,只是刚好名字一样而已,基类根本“看不到”它。
下面分两种场景给你解决方案:
场景1:允许修改基类(最优解)
如果能调整基类代码,最合理的方式是把父类的Output改成虚方法,子类用override替代new,利用多态特性让基类自动调用子类的实现:
class BIG { // 把方法改成virtual,允许子类重写 protected virtual void Output(string text = "") { Console.WriteLine("BIG - " + text); } public void CallingOutput() { Output("Outputting..."); } } class SMALL : BIG { // 用override重写,替换父类的实现 public override void Output(string text = "") { Console.WriteLine("SMALL - " + text); } }
这样不管你是用SMALL实例,还是把SMALL赋值给BIG类型的变量,调用CallingOutput都会输出SMALL - Outputting...,完全符合你的需求。
场景2:无法修改基类(妥协方案)
如果基类是第三方代码不能修改,那可以通过以下两种方式绕开:
方案A:子类重写CallingOutput方法
在子类里重新定义CallingOutput,让它直接调用子类自己的Output:
class SMALL : BIG { public new void Output(string text = "") { Console.WriteLine("SMALL - " + text); } // 重写父类的CallingOutput,指向子类的Output public new void CallingOutput() { Output("Outputting..."); } }
调用时直接用SMALL实例调用CallingOutput就会触发子类的Output。但要注意:如果把SMALL实例赋值给BIG类型的变量,调用CallingOutput还是会走父类的版本,因为CallingOutput不是虚方法。
方案B:通过反射调用(不推荐,仅作备选)
如果必须通过BIG类型的变量触发子类的Output,可以用反射强行调用子类的方法,但这种方式性能差且不够优雅,尽量不用:
BIG bigObj = new SMALL(); // 获取子类的Output方法 var outputMethod = bigObj.GetType().GetMethod("Output", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); // 调用方法 outputMethod?.Invoke(bigObj, new object[] { "Outputting..." });
这个方法会输出SMALL - Outputting...,但除非万不得已,不建议用这种方式。
最后总结一下:new关键字只是隐藏父类方法,不会改变基类内部的调用逻辑;想要基类能调用子类的方法,最好的方式是用虚方法+重写的多态机制,这也是面向对象设计的标准做法。
内容的提问来源于stack exchange,提问作者thithien

