继承是多态的结构基础,多态是继承的行为表现;abstract用于父类只定义契约不实现时,virtual用于提供默认行为且允许覆盖;多态仅在父类引用调用虚/抽象方法时生效。
继承和多态不是两个独立概念,而是一体两面:继承是结构基础,多态是行为表现——没继承,多态无从谈起;没抽象或虚成员,多态就只是普通调用。
abstract?当你在父类中「只定义行为契约,不提供实现」时,就必须用 abstract。比如所有动物都要 MakeSound(),但狗叫、猫叫、鸟鸣各不相同,父类无法写死逻辑。
abstract class 中,且子类非抽象时,override 是强制的new 抽象类实例,哪怕它有构造函数'Animal' does not implement inherited abstract
member 'Animal.MakeSound()' —— 子类忘了加 override 或漏了方法体virtual 而不是 abstract?当父类能提供一个「合理默认行为」,但允许子类按需覆盖时,用 virtual。比如 ToString() 默认返回类型名,子类可重写为返回更友好的格式。
override,也可以不重写,直接沿用父类逻辑base.MethodName()
virtual 的方法写成 private 或 sealed,导致子类无法扩展virtual)会轻微增加虚表查找开销,一般无需担心多态不是“写了 override 就自动多态”,它只在「通过父类引用调用虚/抽象方法」时才触发。关键看变量声明类型,不是实际对象类型。
Animal a1 = new Dog(); Animal a2 = new Cat(); Console.WriteLine(a1.MakeSound()); // 输出 "Woof!" Console.WriteLine(a2.MakeSound()); // 输出 "Meow!"
void Feed(Animal animal) { animal.Eat(); } —— 传 Dog 就执行 Dog.Eat(),传 Bird 就执行 Bird.Eat()
Animal Create(string type) => type switch { "dog" => new Dog(), "cat" => new Cat() }; —— 调用方只需按 Animal 处理,不用关心具体类型Dog d = new Dog(); d.MakeSound(); —— 这走的是静态绑定,不触发多态,哪怕方法是 virtual 或 abstract
virtual、abstract 和 override 成员参与多态;private、static、sealed override 均不参与protected 和 base 在继承链中的真实作用protected 不是“给子类用的 public”,而是「仅限派生类内部访问」的访问修饰符;base 是子类访问父类成员的唯一安全通道。
protected 成员可在子类中直接使用(如 this.Name),但不能通过子类实例访问(new Dog().Name 报错): base(...) 指定base 调用位置不对(必须是构造函数第一行)、或在静态方法里误用 base
protected 暴露内部状态,优先考虑 protected virtual 方法供子类定制行为,而非暴露字段最常被忽略的一点:多态依赖运行时类型信息(RTTI),而 .NET 的 JIT 编译器对虚调用做了高度优化,所以别因“怕慢”而回避 virtual —— 真正的性能瓶颈几乎从来不在这里,而在设计失当导致的深层继承链或过度抽象。