C#继承的最佳实践

4
我有一个普通的类叫做BaseView,它有一个虚方法DisplayView。此方法调用GetHeaderGetBody虚方法从页面获取内容。然后,我会创建一个继承自BaseView的类,并覆盖需要以不同于基类的方式显示内容的方法。
我的问题是,尽管这样很好用,但在运行代码分析时,我被警告不要直接调用虚函数。
我是否应该在基类之上创建另一个类层,并覆盖虚函数,只继承那个类?
直接使用虚方法的缺点是什么?
编辑:警告如下:

CA2214 : Microsoft.Usage:xxx包含一个调用链,结果导致调用类定义的虚方法。请查看以下调用堆栈以防止意外情况。


7
请展示准确的警告信息。 - Daniel Hilgarth
1
在派生类方法实现中添加新的/覆盖关键字,这个警告可能会消失。 - Maheep
3
如果您正在构造函数中调用虚拟方法,请查看此问题:https://dev59.com/k3RB5IYBdhLWcg3w-8A9 - Sebastian Piu
1
@Maheep 使用 new 关键字不会覆盖基类方法,而只是隐藏了基类的实现。 - Vamsi
代码分析消息编号是什么(应该称为CA2214或类似)? - Rich Tebb
我已经在问题中添加了具体的警告。 - Corne Beukes
1个回答

4
我认为问题在于DisplayView是虚拟的,并且它调用虚拟方法。在大多数情况下,虚拟方法被最终方法调用作为改变行为的手段,例如在策略模式中。如果最终方法调用虚拟方法,编译器知道虚拟方法将始终在所有派生类中被调用,因此虚拟方法的存在是有效的。

事实上,您从虚拟函数调用虚拟函数意味着您的设计可能受到质疑:如果DisplayView是虚拟的,那么另一个实现可以覆盖它。当前实现调用虚拟的GetHeader,但是派生类可能不会这样做。因此无法保证GetHeader不是死代码。

这可能就是FxCop要引起注意的地方。它想知道如果您在基类中定义了虚方法(在本例中为GetHeader)是否所有派生实现都会使用它。

我建议将DisplayView设置为最终方法,或以此为基础评估您的设计。


我花了很长时间使用FxCop,其中规则中有一些非常有趣的思考方式,大多数我不情愿地同意! - Joe
但是关于CA2214的MSDN文档指出,如果虚方法不是从构造函数中调用的话,那么这应该是可以的,至少我是这样理解的。 - Vamsi
有趣。这个问题没有提到它是否从构造函数中调用的。是吗? - Joe
@Joe 构造函数中没有调用这些方法。 - Corne Beukes
感谢大家的反馈。我将重构我的类层次结构。回想起来,GetHeader不应该出现在DisplayView的虚拟方法中,因为现在不再是始终显示标题的标准行为。我将为具有标题和没有标题的视图创建覆盖版本的DisplayView。我认为我的设计缺陷在于我假设虚拟方法中应该存在派生类中的默认行为。 - Corne Beukes
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接