发布于2026-07-18 阅读(0)
扫一扫,手机访问
多态这个话题,可以说是C#面向对象里绕不开的机制。哪怕你只是用了个继承、写了个接口,或者碰了一下虚方法,本质上就已经在跟多态打交道了。关键不在于“用不用”,而在于你是否意识到它的存在,以及是否在设计时主动拿它来降低耦合、提升扩展性。

virtual 和 override?场景其实很典型:你有一组类,行为相似但实现各不相同,比如 Circle、Rectangle、Triangle。这时候,你希望用统一的方式去调用各自特有的逻辑,基类的方法就必须设计成可被重写的。否则,问题就来了——Draw() 方法明明在子类里写了新逻辑,调用的却是基类版本,根本原因就是少了 virtual 和 override。
这里有几个关键点必须明确:
virtual 必须加在基类方法声明上,否则子类无法合法使用 overrideoverride,而不是 new。后者只是隐藏,不是多态重写,效果天差地别abstract 更合适——强制子类实现,省得你忘了List 能装不同子类对象?这是运行时多态最直观的体现。CLR 允许派生类对象以基类类型的“身份”存入集合,但调用方法时,仍然按真实类型执行。换句话说,声明类型是 Shape,但实际对象可能是 Circle 实例,typeof(obj) 返回的是 Circle。
这种机制在绘图系统、报表导出器、支付渠道适配器等场景里特别常见——需要一个统一调度多种具体实现的地方。遍历时调用 obj.Draw(),CLR 自动查虚方法表,跳转到 Circle.Draw()。
需要警惕的是,反过来的操作是行不通的:不能把 List 直接赋给 List 变量。泛型协变需要 out 修饰,而且仅适用于接口,比如 IEnumerable。
这么说吧,不是“更高级”,而是适用面不同。接口多态解决的是“能做什么”,继承多态解决的是“是什么”。
核心差异在于:
IDrawable、ISerializable、IComparable),但只能继承一个基类IPaymentService)时,替换实现无需改调用方代码;而继承体系一旦定型,修改基类的影响范围会大得多至于性能影响,几乎可以忽略。编译器对接口调用的内联优化确实不如虚方法直接,但在实际业务代码中,这点差异完全无感。
这个坑很隐蔽,但一旦踩到,调试起来特别费时间。在基类构造函数中调用 virtual 方法,子类重写版本有可能被执行,但此时子类字段还未初始化——典型的“部分构造对象”陷阱。
举个例子:Shape 构造函数里调用了 this.Draw(),而 Circle 的 Radius 字段还在默认值 0,Draw() 却试图用它算面积,结果可想而知。这种问题不会报错,但结果不可预测。
所以,永远避免在构造函数中调用虚成员。如果确实需要触发子类逻辑,可以改用模板方法模式:基类构造函数调用 protected abstract void OnInitialized(),或者干脆延迟到 Initialize() 这类显式初始化方法中再调用。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8