我们什么时候需要函数隐藏
我们应该什么时候使用函数隐藏(new关键字,不明白的请移步MSDN)?
遵守规则
且看如下一段代码:
public class Base { public virtual void Method() { Console.WriteLine("Base: Method()"); } } public class A : Base { public override void Method() { Console.WriteLine("A: Method()"); } } public class B : Base { public new void Method() { Console.WriteLine("B: Method()"); } } public class TestCase { public static void Main(string[] args) { List<Base> testList = new List<Base>(); testList.Push(new A()); testList.Push(new B()); foreach(Base item in testList) { item.Method(); } } }
可以看到,Base类中将Method方法声明为虚方法,就是为了今后的扩展的,其子类如果觉得Base类中Method方法的默认实现不合适的话,可以自行override,从而达到扩展的目的。而类B中由于使用了new关键字而不是override,使得在多态调用时调用的仍是基类中的Method而不是子类中的Method。形象的说,由于B没有遵守“游戏规则”,所以B对于Method的扩展不会在通常情况下起作用。
这里插一句,飞林沙提出“那virtual这个关键字就可以废弃了,永远把这个方法给暴露出来就成了,或者使用abstract”
对于这个观点,前半句我将在下面说明;而使用abstract就要求子类必须override父类中的方法,而不能使用一种默认的实现,如果只是声明为virtual,子类就可以选择是否覆盖父类的这个方法。
Hack类库
个人认为,只有这一种情况我们可能用到new关键字来进行函数隐藏,且看代码:
public class 鱼 { public void Swim() { Console.WriteLine("摆尾巴游"); } } public class 章鱼 : 鱼 { //章鱼没有尾巴,怎么办? public void new Swim() { Console.WriteLine("摆触手游"); } }
显然,在设计“鱼”这个类的时候,没有想到会有鱼没有尾巴,但是我们又确实碰见了没有尾巴的“章鱼”(别较真)。
如果“鱼”这个类是在类库中的话,我们就不能修改这个类,于是我们只能用new关键字进行方法隐藏,从而在表面上差不多的情况下使得“章鱼”有正确的Swim效果。也就是说,我认为,只有在类库设计不当,导致在本应该有virtual声明而没有声明的情况下,才需要子类进行方法隐藏。我不知道我这个观点是不是全面,希望大家补充,还有什么情况下可能会用到方法隐藏。
但是方法隐藏有这样一个问题(第一段中提出),即在以基类的签名调用子类时调用的仍然是基类的方法(如((鱼)章鱼A).Swim())。也就是说,只有在我们知道一个实例是该子类的时候,我们才能够调用这个新声明的方法。既然如此,我们为什么不干脆声明一个新的方法,而选择隐藏基类的方法呢?
讨论原则
需要注意的是,我的问题并不是override和new的区别,而是以下两点:
- 方法隐藏的使用时机是什么?
- 既然只有在知道类型签名的时候才能够调用新声明的方法,我们为什么不干脆声明一个新方法?
在弄明白我问的是什么的情况下,希望大家能够积极讨论,在讨论的时候,认真阅读其他人的观点,对症下药,不要答非所问,俗称打岔。
补充
首先来看一些我个人认为比较有意义的回复:
我觉得如果用了接口的话,那么用显示接口来做是最好的。其实我推荐这种设计方式:<>,<>,<>。
引用飞林沙:
飞林沙的观点在于为什么不使用组合而是使用继承,但是我感觉跟函数隐藏没有什么特别的联系,不知道是不是我理解的不对。
引用周中豪:
的确如此,但是我想知道是不是有一种应该(适合)使用函数隐藏的情况,如果没有的话,是不是应该禁用这个语言特性。
我认为这个例子明显不能说明问题,这个例子中一个人显然应该拥有若干种武器,什么时候应该用什么武器直接切换就是了,范不着这么别扭。
这句话短小精辟的揭示了函数隐藏的本质。
观点相同~
下面评论中Rouper的思想个人觉得比较好,希望大家能够多加关注:)
另外加一个诺贝尔的链接:不要new你的函数。虽然个别语句略显混乱,不过总体上来说说明了一些问题。
下面给大家带来Zhenway的精彩回复!
总结
讨论了这么长时间,我说一下我得出的结论吧,不同意的也不要喷,毕竟大家是可以有不同想法的,不同想法就可以讨论。
我得出的结论有以下两点:
- 函数隐藏只是用于上面Zhenway提到的那种,子类由于调用方便,需要声明一个与父类中同名函数返回值不同的函数,而这两个函数除了返回值不同以外,在逻辑上没有差别:)
- 其他情况不要使用函数隐藏,因为总有更好的解决方案可以解决而没有函数隐藏带来的副作用:)
2012/11/19补充:
在翻旧文的时候突然看到了这篇文章,Zhenway的回复中的内容,现在可以通过协变性完美的解决:
没有方法级的协变性支持,还是做不到更简洁的实现方式。
2012/11/20补充: