理解 C# 抽象類
C#中的抽象類別是一個基本概念,經常引發開發者的提問。 在他的視頻中,"C# 抽象類別——它們是什麼、如何使用它們及最佳實踐",Tim Corey 深入探討了抽象類別是什麼,如何使用它們,以及最佳實踐。 本文總結了他視頻中的要點,並使用了時間戳作為參考。
介紹
Tim (0:00) 解釋了抽象類別經常被質疑的目的、功能和重要性。 他將抽象類別描述為一個介於完整基類和介面之間的結合體,在功能上位於兩者之間。
演示應用程序走過
在 (0:59),Tim 透過一個展示應用來演示抽象類別。 該應用包括一個控制台應用和一個類別庫,其中有兩個模擬資料庫操作的數據訪問類別。 這些類別具有用於載入和儲存數據的方法,Tim 用這些來說明抽象類別、基類和介面之間的相似點和區別。
以下是基類和派生類的初始代碼:
// Base Class Definition
public class DataAccess
{
// Method to load the connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Method to load data
public void LoadData()
{
Console.WriteLine("Loading data...");
}
// Method to save data
public void SaveData()
{
Console.WriteLine("Saving data...");
}
}
// Derived class that inherits from DataAccess
public class SQLDataAccess : DataAccess
{
// Overriding LoadData method to specify SQL data loading
public new void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Overriding SaveData method to specify SQL data saving
public new void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class that inherits from DataAccess
public class SQLiteDataAccess : DataAccess
{
// Overriding LoadData method to specify SQLite data loading
public new void LoadData()
{
Console.WriteLine("Loading SQLite data...");
}
// Overriding SaveData method to specify SQLite data saving
public new void SaveData()
{
Console.WriteLine("Saving SQLite data...");
}
}
// Base Class Definition
public class DataAccess
{
// Method to load the connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Method to load data
public void LoadData()
{
Console.WriteLine("Loading data...");
}
// Method to save data
public void SaveData()
{
Console.WriteLine("Saving data...");
}
}
// Derived class that inherits from DataAccess
public class SQLDataAccess : DataAccess
{
// Overriding LoadData method to specify SQL data loading
public new void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Overriding SaveData method to specify SQL data saving
public new void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class that inherits from DataAccess
public class SQLiteDataAccess : DataAccess
{
// Overriding LoadData method to specify SQLite data loading
public new void LoadData()
{
Console.WriteLine("Loading SQLite data...");
}
// Overriding SaveData method to specify SQLite data saving
public new void SaveData()
{
Console.WriteLine("Saving SQLite data...");
}
}
建立基類
Tim 在 (3:21) 解釋了如何建立基類。 他重構代碼以將公共方法(例如 LoadConnectionString)移到一個名為 DataAccess 的基類中。 通過從此基類繼承,像 SQLDataAccess 和 SQLiteDataAccess 這樣的其他類別可以訪問這些共享的方法,從而減少代碼重複。
將基類變為抽象類別
Tim 在 (5:56) 將基類轉變為抽象類別,以展示其不同之處。 他將 DataAccess 改變成一個抽象類別,防止直接實例化。 相反,只有從此抽象類別繼承的類別(如 SQLiteDataAccess 和 SQLDataAccess)才能實現其方法並使用其共享功能。
以下是 DataAccess 成為抽象類別後的代碼變化:
// Abstract Base Class Definition
public abstract class AbstractDataAccess
{
// Shared method to load connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Abstract methods that must be implemented by derived classes
public abstract void LoadData();
public abstract void SaveData();
}
// Abstract Base Class Definition
public abstract class AbstractDataAccess
{
// Shared method to load connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Abstract methods that must be implemented by derived classes
public abstract void LoadData();
public abstract void SaveData();
}
抽象類別中的介面部分
Tim 在 (8:34) 解釋了抽象類別如何結合介面和基類的特徵。 他在抽象類別中聲明了抽象方法(如 public abstract void LoadData(); 和 public abstract void SaveData();),但不實現它們。 這確保所有派生類必須實現這些方法,類似於介面的運作方式。
覆蓋抽象類別中的方法
Tim 在 (12:56) 討論了如何覆蓋抽象類別中的方法。 他展示了可以在基類中宣告一個方法為 virtual,允許派生類覆蓋它。 這種方法提供了方法在派生類中的實現和擴展靈活性。
以下是顯示方法覆蓋的派生類代碼:
// Derived class from abstract base class
public class SQLDataAccessWithAbstract : AbstractDataAccess
{
// Implementing the abstract LoadData method
public override void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Implementing the abstract SaveData method
public override void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class from abstract base class
public class SQLDataAccessWithAbstract : AbstractDataAccess
{
// Implementing the abstract LoadData method
public override void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Implementing the abstract SaveData method
public override void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
何時使用抽象類別
Tim 在 (16:01) 建議不應每天都使用抽象類別,但在特定情況下是有價值的。 他警告不要僅僅因為兩個類別有相似的代碼而使用抽象類別。 相反,他強調維護"是"關係,並建議在適用的情況下考慮使用輔助方法或類別來處理共享代碼。
以下是展示用法的主程式代碼:
// Main Program
class Program
{
static void Main(string[] args)
{
// Using Base Class
SQLDataAccess sqlData = new SQLDataAccess();
Console.WriteLine(sqlData.LoadConnectionString());
sqlData.LoadData();
sqlData.SaveData();
Console.WriteLine("--------------------------");
// Using Derived Class from Abstract Base Class
SQLDataAccessWithAbstract sqlDataAbstract = new SQLDataAccessWithAbstract();
Console.WriteLine(sqlDataAbstract.LoadConnectionString());
sqlDataAbstract.LoadData();
sqlDataAbstract.SaveData();
Console.WriteLine("--------------------------");
// You can't instantiate Abstract Base Class directly
// AbstractDataAccess abstractData = new AbstractDataAccess();
// Error: Cannot create an instance of the abstract class
}
}
// Main Program
class Program
{
static void Main(string[] args)
{
// Using Base Class
SQLDataAccess sqlData = new SQLDataAccess();
Console.WriteLine(sqlData.LoadConnectionString());
sqlData.LoadData();
sqlData.SaveData();
Console.WriteLine("--------------------------");
// Using Derived Class from Abstract Base Class
SQLDataAccessWithAbstract sqlDataAbstract = new SQLDataAccessWithAbstract();
Console.WriteLine(sqlDataAbstract.LoadConnectionString());
sqlDataAbstract.LoadData();
sqlDataAbstract.SaveData();
Console.WriteLine("--------------------------");
// You can't instantiate Abstract Base Class directly
// AbstractDataAccess abstractData = new AbstractDataAccess();
// Error: Cannot create an instance of the abstract class
}
}
結論
Tim Corey 對 C# 抽象類別 的深入研究提供了對其目的、功能及實際應用的清晰和實用的理解。 透過他的展示應用,他展示了抽象類別如何在基類和介面之間架橋,讓開發者能夠創建靈活且易於維護的代碼結構。
通過強調最佳實踐,諸如在適當情境下使用抽象類別並避免過度使用,Tim 為開發者提供了做出明智設計選擇的工具。 掌握這些概念後,開發者可以增強其面向對象編程的技能,並構建強大的 C# 應用。
