0
点赞
收藏
分享

微信扫一扫

《继承与多态:代码复用(五)》

一、基类与派生类:单一继承的层次化设计

核心规则

  • 单一继承限制:C#中类仅支持单继承(class Derived : Base),但可通过接口(interface)实现多重行为扩展。
  • 继承链传递性:若类C继承BB继承A,则C自动继承AB的成员(私有成员除外)。
  • 访问修饰符影响
  • 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):运行时多态的核心

  • 基类要求:方法需标记为virtualabstract
  • 派生类规则:使用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"

关键区别

维度

重写(override

隐藏(new

绑定时机

运行时(动态多态)

编译时(静态覆盖)

基类方法

必须为virtual/abstract

无需特殊标记

使用场景

框架扩展、业务多态(如支付系统)

静态逻辑拓展、工具类辅助方法

三、抽象类与接口的对比:代码共享 vs 行为统一

1. 抽象类(abstract class

  • 目的:为相关类提供共通基类,包含具体实现和抽象成员。
  • 特性
  • 不能直接实例化,必须通过非抽象派生类实例化。
  • 可包含抽象方法(无实现,强制派生类重写)和非抽象方法(有实现)。
  • 支持字段、属性、构造函数(但构造函数不能直接调用,仅用于派生类初始化)。
  • 访问修饰符:成员可为publicprotectedinternalprivate

代码示例

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且不可指定访问修饰符。
  • 一个类可实现多个接口(形式上的多重继承)。
  • 使用场景:定义完全不相关类的共同行为(如IEnumerableIDisposable)。

代码示例

csharp
 public interface ILogger { 
 
     void Log(string message); 
 
 }  
 
  
 
 public class FileLogger : ILogger { 
 
     public void Log(string message) => File.WriteAllText("log.txt", message); 
 
 }

3. 关键对比

维度

抽象类

接口

继承限制

单继承

多实现(一个类可实现多个接口)

成员类型

字段、属性、方法、构造函数

方法、属性、事件、索引器(无字段)

实现要求

派生类必须实现抽象方法(除非派生类也是抽象类)

实现类必须提供所有成员的实现

访问修饰符

支持完整访问修饰符集

成员默认public,不可指定

适用场景

紧密相关类的代码共享(如VehicleCar

跨类行为统一(如IEnumerable

四、最佳实践与避坑指南

  1. 优先使用组合而非继承:过度继承会导致层次结构复杂化,优先通过依赖注入或组合模式实现代码复用。
  2. 明确区分overridenew
  • 若需多态,使用override
  • 若需静态覆盖且不破坏基类逻辑,使用new(但需显式声明以避免编译警告)。
  1. 抽象类与接口的选择
  • 需要代码共享时选抽象类(如框架基类)。
  • 需要跨类行为统一时选接口(如插件系统)。
  1. 避免隐藏基类方法:隐藏可能导致意外行为(如通过基类引用调用时执行基类方法),优先通过重写或新增方法实现扩展。

五、总结与进阶方向

  • 继承与多态:理解单一继承、方法重写与隐藏的差异,是掌握面向对象设计的基础。
  • 抽象类与接口:根据代码共享需求和行为统一需求选择合适工具,平衡灵活性与可维护性。
  • 进阶探索
  • 密封类(sealed)与密封方法:防止进一步继承或重写。
  • 显式接口实现:通过InterfaceName.MethodName语法实现接口成员,避免命名冲突。
  • 模式匹配:结合isswitch表达式优化继承层次结构的类型检查。

通过系统掌握继承与多态的核心机制,你将能够设计出更灵活、可扩展的面向对象系统,为后续学习设计模式、依赖注入等高级主题奠定坚实基础。

举报

相关推荐

0 条评论