我們一起聊聊 C# 方法重寫(override)
方法重寫(Method Overriding)是 C# 面向?qū)ο缶幊讨械囊粋€核心概念,它允許派生類根據(jù)需要改變繼承自基類的方法實現(xiàn)。通過方法重寫,可以實現(xiàn)多態(tài)性,使程序更加靈活、易于擴展和維護。本文將詳細介紹方法重寫的特性和應用場景,并通過多個實例加深對這一概念的理解。
實現(xiàn)項目中方法重寫比較常用。
方法重寫的特性
多態(tài)性
- 定義:多態(tài)性允許相同的接口調(diào)用,根據(jù)對象的實際類型執(zhí)行不同的操作。
- 實現(xiàn):通過在基類中定義虛方法(virtual),在派生類中重寫(override)這些方法來實現(xiàn)多態(tài)。
靈活性
- 重寫基類的方法,派生類可以根據(jù)自身需求修改或擴展方法的實現(xiàn),而無需修改基類的代碼。
可維護性
- 通過方法重寫,可以在不改變基類代碼的情況下,對其行為進行修改或擴展,增強了代碼的可維護性和可擴展性。
方法重寫的應用場景
定制化行為
當派生類需要提供與基類不同的具體實現(xiàn)時,可通過重寫基類的方法來實現(xiàn)定制化行為。
擴展基類功能
在不改變基類的情況下,派生類可以通過調(diào)用基類方法并添加新的功能來擴展基類的行為。
實現(xiàn)設計模式
某些設計模式(如模板方法模式)需要通過在派生類中重寫基類方法來實現(xiàn)特定的業(yè)務邏輯。
方法重寫的語法與規(guī)則
- 基類方法:使用 virtual 關鍵字標識可被重寫的方法。
public virtual void MethodName()
{
// 基類方法實現(xiàn)
}
- 派生類方法:使用 override 關鍵字重寫基類的方法。
public override void MethodName()
{
// 派生類方法實現(xiàn)
}
- 調(diào)用基類方法:在重寫的方法中,可以使用 base.MethodName() 調(diào)用基類的方法實現(xiàn)。
實例解析
示例 1:動物叫聲
目標:演示如何通過方法重寫實現(xiàn)多態(tài)性。
namespace App06
{
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物發(fā)出聲音。");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("狗叫:汪汪!");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("貓叫:喵喵!");
}
}
internal class Program
{
static void Main(string[] args)
{
// 使用示例
Animal[] animals = { new Dog(), new Cat(), new Animal() };
foreach (var animal in animals)
{
animal.Speak();
}
}
}
}
說明:
- 基類 Animal 定義了虛方法 Speak()。
- Dog 和 Cat 類重寫了 Speak() 方法,實現(xiàn)各自的叫聲。
- 通過基類引用調(diào)用 Speak() 方法,根據(jù)實際對象類型執(zhí)行不同的實現(xiàn)。
示例 2:車輛啟動
目標:展示如何在重寫的方法中調(diào)用基類的方法。
namespace App06
{
public class Vehicle
{
public virtual void Start()
{
Console.WriteLine("車輛啟動。");
}
}
public class Car : Vehicle
{
public override void Start()
{
base.Start(); // 調(diào)用基類的 Start 方法
Console.WriteLine("汽車發(fā)動引擎。");
}
}
internal class Program
{
static void Main(string[] args)
{
// 使用示例
Vehicle myCar = new Car();
myCar.Start();
}
}
}
說明:
- Car 類重寫了 Start() 方法,并使用 base.Start() 調(diào)用了基類的方法,實現(xiàn)了功能的擴展。
示例 3:圖形繪制
目標:演示抽象類和方法重寫的結合使用。
namespace App06
{
public abstract class Shape
{
public abstract void Draw();
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("繪制一個圓形。");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("繪制一個矩形。");
}
}
internal class Program
{
static void Main(string[] args)
{
// 使用示例
List<Shape> shapes = new List<Shape>
{
new Circle(),
new Rectangle()
};
foreach (var shape in shapes)
{
shape.Draw();
}
}
}
}
說明:
- 抽象類 Shape 定義了抽象方法 Draw(),強制派生類必須實現(xiàn)該方法。
- 派生類 Circle 和 Rectangle 實現(xiàn)了各自的 Draw() 方法。
示例 4:員工薪資計算
目標:通過方法重寫計算不同類型員工的薪資。
namespace App06
{
public class Employee
{
public virtual double CalculateSalary()
{
return 3000.0; // 基本工資
}
}
public class Manager : Employee
{
public override double CalculateSalary()
{
return base.CalculateSalary() + 2000.0; // 基本工資 + 管理獎金
}
}
public class Salesperson : Employee
{
public double SalesAmount { get; set; }
public override double CalculateSalary()
{
return base.CalculateSalary() + SalesAmount * 0.05; // 基本工資 + 銷售提成
}
}
internal class Program
{
static void Main(string[] args)
{
// 使用示例
Employee emp1 = new Manager();
Employee emp2 = new Salesperson { SalesAmount = 50000 };
Console.WriteLine($"經(jīng)理薪資:{emp1.CalculateSalary()}");
Console.WriteLine($"銷售員薪資:{emp2.CalculateSalary()}");
}
}
}
說明:
- Manager 和 Salesperson 重寫了 CalculateSalary() 方法,分別計算不同的薪資。
- 使用基類引用調(diào)用方法,實現(xiàn)了對不同員工薪資的多態(tài)計算。
示例 5:模板方法模式
目標:通過方法重寫實現(xiàn)模板方法模式。
namespace App06
{
public abstract class DataProcessor
{
// 模板方法
public void ProcessData()
{
ReadData();
Process();
SaveData();
}
protected abstract void ReadData();
protected abstract void Process();
protected virtual void SaveData()
{
Console.WriteLine("數(shù)據(jù)已保存。");
}
}
public class ExcelDataProcessor : DataProcessor
{
protected override void ReadData()
{
Console.WriteLine("從 Excel 文件讀取數(shù)據(jù)。");
}
protected override void Process()
{
Console.WriteLine("處理 Excel 數(shù)據(jù)。");
}
}
public class CsvDataProcessor : DataProcessor
{
protected override void ReadData()
{
Console.WriteLine("從 CSV 文件讀取數(shù)據(jù)。");
}
protected override void Process()
{
Console.WriteLine("處理 CSV 數(shù)據(jù)。");
}
protected override void SaveData()
{
Console.WriteLine("數(shù)據(jù)已保存到 CSV 文件。");
}
}
internal class Program
{
static void Main(string[] args)
{
// 使用示例
DataProcessor processor1 = new ExcelDataProcessor();
processor1.ProcessData();
DataProcessor processor2 = new CsvDataProcessor();
processor2.ProcessData();
}
}
}
圖片
說明:
- 基類 DataProcessor 定義了模板方法 ProcessData(),并聲明了一些抽象方法。
- 派生類實現(xiàn)了這些抽象方法,定制了具體的處理步驟。
注意事項
- 方法簽名一致:重寫的方法必須與基類的方法具有相同的簽名,包括方法名、參數(shù)類型和返回類型。
- 訪問修飾符:重寫的方法的訪問級別不能低于基類中被重寫的方法。
- sealed 關鍵字:如果不希望派生類進一步重寫某個方法,可以在方法前添加 sealed 關鍵字。
public override sealed void MethodName()
{
// 實現(xiàn)
}
總結
方法重寫是實現(xiàn)多態(tài)性的關鍵,可以使代碼更加靈活和可維護。通過在派生類中重寫基類的方法,我們可以根據(jù)需要改變或擴展基類的行為,而無需修改基類的代碼。在設計類的繼承結構時,合理地使用方法重寫,可以提高代碼的質(zhì)量和可擴展性。
建議:
- 在基類中,預期可能被修改的方法應聲明為 virtual 或 abstract。
- 在派生類中,重寫方法時確保方法簽名正確,并考慮是否需要調(diào)用 base 方法。
- 謹慎使用方法重寫,避免造成代碼的混亂和難以維護。