一、基类与派生类:单一继承的层次化设计
核心规则:
- 单一继承限制:C#中类仅支持单继承(
class Derived : Base
),但可通过接口(interface
)实现多重行为扩展。 - 继承链传递性:若类
C
继承B
,B
继承A
,则C
自动继承A
和B
的成员(私有成员除外)。 - 访问修饰符影响:
public
:所有代码可访问。protected
:仅类内部和派生类可访问。private
:仅类内部可访问,派生类无法继承。
代码示例:
csharp
public class Vehicle {
public string Make { get; set; }
public void StartEngine() => Console.WriteLine("Engine started.");
}
public class Car : Vehicle {
public int NumberOfDoors { get; set; }
public void HonkHorn() => Console.WriteLine("Horn honked.");
}
// 使用
Car car = new Car();
car.StartEngine(); // 继承自Vehicle
car.HonkHorn(); // Car特有方法
二、方法重写(virtual/override
)与隐藏(new
):多态 vs 静态覆盖
1. 重写(override
):运行时多态的核心
- 基类要求:方法需标记为
virtual
或abstract
。 - 派生类规则:使用
override
关键字,方法签名(名称、参数、返回类型)必须与基类一致。 - 行为:通过基类引用调用时,实际执行派生类的重写方法(动态绑定)。
代码示例:
csharp
public class Animal {
public virtual void Speak() => Console.WriteLine("Animal sound");
}
public class Dog : Animal {
public override void Speak() => Console.WriteLine("Dog barks");
}
// 使用
Animal animal = new Dog();
animal.Speak(); // 输出"Dog barks"(多态)
2. 隐藏(new
):编译时静态覆盖
- 基类要求:方法无需特殊标记(普通方法即可)。
- 派生类规则:使用
new
关键字(可省略,但显式声明更清晰),方法签名与基类相同。 - 行为:通过基类引用调用时,执行基类方法;通过派生类引用调用时,执行派生类方法(静态绑定)。
代码示例:
csharp
public class Shape {
public void Draw() => Console.WriteLine("Base shape");
}
public class Circle : Shape {
public new void Draw() => Console.WriteLine("Circle drawn");
}
// 使用
Shape shape = new Circle();
shape.Draw(); // 输出"Base shape"(编译时绑定)
Circle circle = new Circle();
circle.Draw(); // 输出"Circle drawn"
关键区别:
维度 | 重写( | 隐藏( |
绑定时机 | 运行时(动态多态) | 编译时(静态覆盖) |
基类方法 | 必须为 | 无需特殊标记 |
使用场景 | 框架扩展、业务多态(如支付系统) | 静态逻辑拓展、工具类辅助方法 |
三、抽象类与接口的对比:代码共享 vs 行为统一
1. 抽象类(abstract class
)
- 目的:为相关类提供共通基类,包含具体实现和抽象成员。
- 特性:
- 不能直接实例化,必须通过非抽象派生类实例化。
- 可包含抽象方法(无实现,强制派生类重写)和非抽象方法(有实现)。
- 支持字段、属性、构造函数(但构造函数不能直接调用,仅用于派生类初始化)。
- 访问修饰符:成员可为
public
、protected
、internal
、private
。
代码示例:
csharp
public abstract class Payment {
public abstract void ProcessPayment(); // 抽象方法,强制派生类实现
public void LogTransaction() => Console.WriteLine("Transaction logged."); // 非抽象方法
}
public class CreditCardPayment : Payment {
public override void ProcessPayment() => Console.WriteLine("Credit card processed.");
}
2. 接口(interface
)
- 目的:定义跨类行为的统一契约,不关心具体实现。
- 特性:
- 不能包含字段、构造函数。
- 从C# 8.0开始支持默认实现(
default
),但成员默认仍为public
且不可指定访问修饰符。 - 一个类可实现多个接口(形式上的多重继承)。
- 使用场景:定义完全不相关类的共同行为(如
IEnumerable
、IDisposable
)。
代码示例:
csharp
public interface ILogger {
void Log(string message);
}
public class FileLogger : ILogger {
public void Log(string message) => File.WriteAllText("log.txt", message);
}
3. 关键对比:
维度 | 抽象类 | 接口 |
继承限制 | 单继承 | 多实现(一个类可实现多个接口) |
成员类型 | 字段、属性、方法、构造函数 | 方法、属性、事件、索引器(无字段) |
实现要求 | 派生类必须实现抽象方法(除非派生类也是抽象类) | 实现类必须提供所有成员的实现 |
访问修饰符 | 支持完整访问修饰符集 | 成员默认 |
适用场景 | 紧密相关类的代码共享(如 | 跨类行为统一(如 |
四、最佳实践与避坑指南
- 优先使用组合而非继承:过度继承会导致层次结构复杂化,优先通过依赖注入或组合模式实现代码复用。
- 明确区分
override
与new
:
- 若需多态,使用
override
。 - 若需静态覆盖且不破坏基类逻辑,使用
new
(但需显式声明以避免编译警告)。
- 抽象类与接口的选择:
- 需要代码共享时选抽象类(如框架基类)。
- 需要跨类行为统一时选接口(如插件系统)。
- 避免隐藏基类方法:隐藏可能导致意外行为(如通过基类引用调用时执行基类方法),优先通过重写或新增方法实现扩展。
五、总结与进阶方向
- 继承与多态:理解单一继承、方法重写与隐藏的差异,是掌握面向对象设计的基础。
- 抽象类与接口:根据代码共享需求和行为统一需求选择合适工具,平衡灵活性与可维护性。
- 进阶探索:
- 密封类(
sealed
)与密封方法:防止进一步继承或重写。 - 显式接口实现:通过
InterfaceName.MethodName
语法实现接口成员,避免命名冲突。 - 模式匹配:结合
is
和switch
表达式优化继承层次结构的类型检查。
通过系统掌握继承与多态的核心机制,你将能够设计出更灵活、可扩展的面向对象系统,为后续学习设计模式、依赖注入等高级主题奠定坚实基础。