基类引用的本质
在C#面向对象编程中,派生类对象由基类部分和扩展部分组成。通过基类引用访问派生类对象时,实际是在进行「观察视角」的转换:
MyDerivedClass derived = new MyDerivedClass();
MyBaseClass mybc = (MyBaseClass)derived; // 视角转换
- 🔑 基类引用只能访问继承链中的成员
- ⚠️ 无法通过基类引用访问派生类新增成员(如示例中的var1)
- 🖼️ 内存布局:基类部分始终位于对象内存起始位置
虚方法的多态魔法
当使用virtual和override组合时,将实现真正的多态行为:
// 基类定义
virtual public void Print() { ... }
// 派生类覆写
override public void Print() { ... }
✅ 调用链规则:
- 通过基类引用调用时,自动寻找最高派生版本的实现
- 实际执行的是运行时类型的方法(动态绑定)
🚫 常见错误:
- 覆写方法访问级别不一致
- 试图覆写非虚方法
- 忘记override使用new导致隐藏而非覆写
多层继承中的覆写策略
在三级继承体系中,覆写行为呈现链式反应:
class Base { virtual void Print() }
class Derived : Base { override void Print() }
class SecondDerived : Derived { ... }
修饰符 | 调用结果 | 内存表现 |
---|---|---|
override | 执行最顶层派生类方法 | 法表指针指向最新实现 |
new | 停留在当前继承层级的实现 | 创建独立方法槽,与基类实现并存 |
实战对比:override vs new
通过代码实验验证不同修饰符的效果差异:
// 情况1:使用override
SecondDerived.Print => "SecondDerived"
mybc.Print => "SecondDerived"
// 情况2:使用new
SecondDerived.Print => "SecondDerived"
mybc.Print => "Derived"
📊 执行原理图解:
- override形成方法调用链
- new创建独立方法分支
- 虚方法表(vtable)的结构差异
扩展应用:属性与索引器的覆写
多态机制不仅限于方法,同样适用于属性:
class Base {
virtual int MyProp { get { return 5; } }
}
class Derived : Base {
override int MyProp { get { return 10; } }
}
📌 属性覆写需保持:
- 相同的名称和返回类型
- 一致的访问器签名(get/set)
💡 索引器覆写同理:
- 保持参数列表一致
- 使用virtual/override修饰
最佳实践指南
合理设计虚方法:
- 将可能变化的逻辑声明为virtual
- 保持基类方法的稳定性
谨慎使用new修饰符:
- 仅当需要刻意隐藏基类实现时使用
- 添加XML注释说明隐藏意图
多态设计原则:
- 遵循里氏替换原则
- 通过抽象基类定义契约
- 使用protected保护核心实现
关键理解:多态的本质是通过类型引用与对象实体的分离,实现「同一接口,多种实现」的面向对象特性。掌握虚方法机制,能显著提升代码的扩展性和可维护性。