2024-11-02 022-01_Ccharp從最基本的編程概念到面向物件程式設計
2024-11-02 022-01_Ccharp從最基本的編程概念到面向物件程式設計
了解,讓我們從最基本的編程概念開始,一步步深入了解 C# 中類別(Classes)的語法構造及其背後的設計理念。我們將從傳統的程序式編程(Procedural Programming)開始,逐漸過渡到面向物件程式設計(Object-Oriented Programming, OOP),並解釋為什麼要引入類別以及其各種特性。
目錄
- 程序式編程的基礎
- 程序式編程的限制
- 引入類別與物件
- 類別的基本構造
- 封裝與存取修飾符
- 繼承(Inheritance)
- 多態性(Polymorphism)
- 類別設計的進階概念
- 小結
1. 程序式編程的基礎
程序式編程(Procedural Programming) 是一種基於程序(函數或方法)來組織代碼的編程範式。在這種方式下,程序被視為一系列指令,按照順序執行。
範例:簡單的程序式代碼
1 | using System; |
輸出範例:
1 | 請輸入你的名字: Alice |
解析:
- 程式由一個
Main
方法開始執行,依次呼叫GetName
、GetAge
和DisplayInfo
方法。 - 每個方法負責一個特定的功能,如獲取使用者輸入或顯示信息。
2. 程序式編程的限制
隨著程式的規模增大,程序式編程可能會面臨以下挑戰:
代碼重用困難:
- 當需要多次使用相同的功能時,可能會重複編寫相似的代碼,導致代碼冗餘和難以維護。
維護困難:
- 大型程序中,找到特定功能的代碼位置變得困難,修改一處代碼可能影響到其他部分。
數據與功能的分離:
- 數據(變數)和操作數據的函數是分開的,缺乏組織,難以理解數據和功能之間的關係。
缺乏模組化:
- 難以將程式劃分為獨立的模組或部分,每個部分都可以獨立開發和測試。
範例:代碼重用困難
假設我們有多個不同的地方需要獲取使用者的名字和年齡,可能會重複呼叫 GetName
和 GetAge
方法,但這些方法並沒有組織在一起,難以管理。
3. 引入類別與物件
為了解決上述問題,面向物件程式設計(OOP) 被引入。OOP 將數據和操作數據的功能組織在一起,形成物件(Objects),並通過類別(Classes)來定義物件的結構和行為。
類別與物件的概念
- 類別(Class):類別是物件的藍圖,定義了物件的屬性(數據)和方法(功能)。
- 物件(Object):物件是類別的實例,擁有類別定義的屬性和方法。
為什麼使用類別?
代碼重用:
- 通過定義類別,可以創建多個物件,重複使用相同的代碼。
封裝:
- 將數據和操作數據的方法組織在一起,保護數據不被外部直接訪問或修改。
模組化:
- 將程式劃分為獨立的類別,每個類別負責特定的功能,提升代碼的可維護性和可讀性。
繼承與多態性:
- 通過繼承,可以創建更具特化性的類別,並實現多態性,增強靈活性。
範例:轉換為類別
讓我們將之前的程序式代碼轉換為面向物件的方式。
1 | using System; |
輸出範例:
1 | 請輸入你的名字: Bob |
解析:
- 定義了一個
Person
類別,包含Name
和Age
屬性,以及GetName
、GetAge
和DisplayInfo
方法。 - 在
Main
方法中,創建了一個Person
物件,並呼叫其方法來獲取和顯示信息。 - 這樣的結構更具組織性,且易於擴展和維護。
4. 類別的基本構造
類別的結構
一個完整的 C# 類別通常包含以下部分:
修飾符(Modifiers):
- 定義類別的可見性和其他屬性,如
public
、private
、abstract
等。
- 定義類別的可見性和其他屬性,如
類別名稱(Class Name):
- 遵循命名規則,通常使用 Pascal 命名法。
繼承(Inheritance):
- 指定類別從哪個基類繼承,使用冒號
:
。
- 指定類別從哪個基類繼承,使用冒號
介面實作(Interface Implementation):
- 指定類別實作哪些介面。
成員(Members):
- 包含欄位(Fields)、屬性(Properties)、方法(Methods)、建構函數(Constructors)等。
範例:類別的基本構造
1 | public class Car |
解析:
public class Car
:定義了一個公開的Car
類別。- 欄位:
private string model;
:私有欄位,只能在Car
類別內部訪問。
- 屬性:
public string Make { get; set; }
:公開屬性,提供對Make
的讀寫訪問。public string Model
:公開屬性,通過get
和set
訪問私有欄位model
。
- 建構函數:
public Car(string make, string model)
:初始化Make
和Model
屬性。
- 方法:
public void DisplayInfo()
:顯示車輛信息。
5. 封裝與存取修飾符
封裝(Encapsulation) 是 OOP 的一個核心概念,旨在將數據(屬性)和操作數據的方法封裝在一起,並控制對這些數據的訪問。
存取修飾符(Access Modifiers)
C# 提供了多種存取修飾符來控制類別及其成員的可見性:
**
public
**:- 可被任何其他程式碼訪問。
- 適用於希望公開的類別或成員。
**
private
**:- 僅在定義它的類別內部可訪問。
- 適用於不希望外部直接訪問的成員。
**
protected
**:- 可在定義它的類別及其派生類別中訪問。
- 適用於需要在子類中訪問的成員。
**
internal
**:- 僅在同一個組件(Assembly)內部可訪問。
- 適用於類別或成員只供內部使用的情況。
**
protected internal
**:- 可在同一組件內部及任何派生類別中訪問。
**
private protected
**(C# 7.2 及以上):- 可在同一組件內部及其派生類別中訪問。
- 更嚴格的修飾符,限制訪問範圍。
範例:封裝與存取修飾符
1 | public class BankAccount |
輸出:
1 | 存入: 500, 新餘額: 1500 |
解析:
- **私有欄位
balance
**:- 只能在
BankAccount
類別內部訪問,外部無法直接修改。
- 只能在
- **公開屬性
Balance
**:- 提供對
balance
的讀取權限(get
),但寫入權限(set
)是私有的,只能在類別內部修改。
- 提供對
- **公開方法
Deposit
和Withdraw
**:- 提供了控制
balance
修改的方法,確保金額的有效性。
- 提供了控制
- **私有方法
LogTransaction
**:- 僅供
BankAccount
類別內部使用,用於記錄交易(此範例中未實作)。
- 僅供
優點:
- 資訊隱藏:
- 外部程式碼無法直接訪問或修改
balance
,只能通過公開的方法進行操作,確保數據的一致性和安全性。
- 外部程式碼無法直接訪問或修改
- 控制權限:
- 使用公開屬性和方法來控制對數據的訪問和修改,防止不當操作。
6. 繼承(Inheritance)
繼承(Inheritance) 是 OOP 的一個重要特性,允許你基於現有的類別創建新的類別。新類別(派生類別)繼承自現有的類別(基類),並可以擴展或修改其行為。
為什麼需要繼承?
代碼重用:
- 重用基類中的屬性和方法,避免重複編寫相同的代碼。
建立類別層次結構:
- 將相關的類別組織在一起,形成層次化的結構,提高代碼的可理解性。
多態性:
- 通過基類引用來操作派生類物件,實現靈活的代碼設計。
繼承的語法
使用冒號 :
來指定繼承的基類。
範例:基類與派生類
1 | using System; |
輸出:
1 | Buddy 正在吃食物。 |
解析:
- **基類
Animal
**:- 定義了
Name
屬性和Eat
方法。 - 定義了一個虛擬方法
Speak
,允許派生類覆蓋。
- 定義了
- **派生類
Dog
**:- 繼承自
Animal
。 - 覆蓋了基類的
Speak
方法,提供了特定的實現。 - 定義了一個派生類特有的方法
Fetch
。
- 繼承自
- 在
Main
方法中:- 創建了一個
Dog
物件myDog
,並呼叫其方法。 - 使用基類引用
myAnimal
指向myDog
物件,展示了多態性。 - 注意:基類引用無法訪問派生類特有的方法,如
Fetch
。
- 創建了一個
優點:
- 代碼重用:
Dog
繼承了Animal
的Name
屬性和Eat
方法,無需重新定義。 - 多態性:可以使用基類引用來操作派生類物件,實現靈活的代碼設計。
- 擴展性:可以在派生類中新增特有的屬性和方法,擴展基類的功能。
7. 多態性(Polymorphism)
多態性(Polymorphism) 是 OOP 的另一個核心概念,允許相同的接口在不同的類別中有不同的實現。這使得程式碼更加靈活和可擴展。
為什麼需要多態性?
靈活性:
- 能夠使用基類引用來操作不同的派生類物件,減少程式碼的耦合度。
可擴展性:
- 新增派生類時,不需要修改使用基類引用的現有代碼。
代碼簡潔:
- 通過統一的接口來操作不同的類別,減少了代碼的冗餘。
多態性的實現
在 C# 中,多態性通常通過以下兩個步驟實現:
基類中的虛擬方法:
- 使用
virtual
關鍵字標記基類中的方法,允許派生類覆蓋。
- 使用
派生類中的覆蓋方法:
- 使用
override
關鍵字在派生類中覆蓋基類的方法,提供具體的實現。
- 使用
範例:多態性的應用
1 | using System; |
輸出:
1 | 圓形A 的半徑是 5,面積是 78.54。 |
解析:
- **基類
Shape
**:- 定義了
Name
屬性和虛擬方法Area
和Display
。
- 定義了
- **派生類
Circle
和Rectangle
**:- 覆蓋了基類的
Area
方法,提供了具體的計算邏輯。 - 覆蓋了基類的
Display
方法,提供了更具體的信息顯示。
- 覆蓋了基類的
- 在
Main
方法中:- 創建了一個包含不同派生類物件的
List<Shape>
。 - 遍歷這個列表,呼叫每個物件的
Display
方法,根據實際物件的類型執行相應的覆蓋方法。
- 創建了一個包含不同派生類物件的
優點:
- 靈活性:可以使用基類引用來處理不同的派生類物件,實現統一的接口。
- 可擴展性:新增派生類時,無需修改使用基類引用的現有代碼。
- 代碼簡潔:通過基類的方法呼叫,減少了重複代碼。
8. 類別設計的進階概念
封裝(Encapsulation)
封裝 是將數據(屬性)和操作數據的方法組織在一起,並控制對數據的訪問。這有助於保護數據不被外部不當修改,並確保物件的一致性。
抽象化(Abstraction)
抽象化 是指僅展示物件的必要特性,隱藏其內部實現細節。這有助於簡化複雜性,使使用者更容易理解和使用物件。
類別設計原則
單一職責原則(Single Responsibility Principle):
- 每個類別應該只有一個單一的職責,並且該職責應該被完整地封裝在類別中。
開放封閉原則(Open/Closed Principle):
- 類別應該對擴展開放,對修改封閉。即應該能夠通過繼承或組合來擴展類別的功能,而不需要修改原有的類別。
里氏替換原則(Liskov Substitution Principle):
- 派生類別的物件應該能夠替換其基類別的物件,而不影響程式的正確性。
接口隔離原則(Interface Segregation Principle):
- 不應該強迫類別依賴它們不使用的接口。應該將大型接口分割成更小、更具專一性的接口。
依賴反轉原則(Dependency Inversion Principle):
- 高層模組不應依賴低層模組,兩者應該依賴於抽象(介面或抽象類別)。
範例:良好設計的類別
1 | using System; |
輸出:
1 | Log: 應用程式啟動 |
解析:
- **介面
IPrinter
**:- 定義了一個
Print
方法,抽象化了打印的行為。
- 定義了一個
- **類別
ConsolePrinter
**:- 實作了
IPrinter
介面,具體實現了將信息打印到控制台。
- 實作了
- **類別
Logger
**:- 將打印的責任委託給
IPrinter
介面,遵循了依賴反轉原則。 - 這樣的設計允許未來更換不同的打印方式(如文件打印)而不需要修改
Logger
類別。
- 將打印的責任委託給
- 在
Main
方法中:- 創建了
ConsolePrinter
和Logger
物件,並使用Logger
來記錄信息。
- 創建了
優點:
- 高內聚低耦合:每個類別專注於自己的職責,通過介面進行通信,減少了類別之間的耦合。
- 可擴展性:未來可以輕鬆添加新的打印方式,只需創建新的
IPrinter
實作,而不需修改Logger
。 - 可測試性:可以輕鬆地為
Logger
類別創建模擬的IPrinter
來進行單元測試。
9. 小結
通過這一步步的演進,我們從程序式編程的基礎開始,逐漸引入了類別和物件,並深入了解了封裝、繼承和多態性等 OOP 的核心概念。以下是本章內容的快速回顧:
程序式編程的基礎:
- 基於函數和指令的編程方式,適用於簡單的任務,但隨著程式規模增大,維護和重用變得困難。
程序式編程的限制:
- 代碼重用困難、維護困難、數據與功能的分離、缺乏模組化等問題。
引入類別與物件:
- 通過類別組織數據和功能,實現代碼重用、封裝和模組化,提升程式的可維護性和可讀性。
類別的基本構造:
- 學習類別的修飾符、名稱、繼承、介面實作、成員(欄位、屬性、方法、建構函數等)的結構。
封裝與存取修飾符:
- 使用存取修飾符控制類別及其成員的可見性,實現資訊隱藏和封裝。
繼承(Inheritance):
- 基於基類創建派生類,實現代碼重用和建立類別層次結構。
多態性(Polymorphism):
- 透過基類引用操作派生類物件,實現靈活的代碼設計和減少耦合。
類別設計的進階概念:
- 掌握封裝、抽象化、以及 SOLID 原則等,設計出高內聚低耦合的類別。
總結
了解 C# 中類別的語法構造及其設計理念,有助於你編寫出結構良好、易於維護和擴展的程式碼。通過封裝、繼承和多態性等 OOP 概念,你可以創建出更靈活、可重用和高效的應用程式。隨著你不斷練習和應用這些概念,你將能夠更自信地設計和實現複雜的軟體系統。
如果你有任何進一步的問題,或需要更詳細的範例和解釋,請隨時告訴我!