【C#進階】高階物件導向特性_2024-06-22

StarYou發表於2024-06-22

一、概念

1. 高階物件導向特性

物件導向程式設計(OOP)是一種程式設計正規化,它使用“物件”來設計軟體。這些物件可以包含資料和行為。高階物件導向特性包括:

  • 封裝:把資料和操作這些資料的程式碼打包在一起,不讓外部直接訪問資料,而是透過方法來操作。
  • 繼承:允許新建立的類(子類)繼承現有類(父類)的屬性和方法,但可以新增或重寫自己的特性。
  • 多型:讓不同的物件可以對同一訊息做出響應,但具體的行為會根據物件的實際型別而有所不同。

2. 介面和抽象類

  • 介面(Interface):可以想象成一個“合同”,它規定了類必須實現的方法和屬性,但不提供實現細節。任何類都可以實現多個介面。
  • 抽象類(Abstract Class):是一個不能被例項化的類,通常包含一些抽象方法(沒有實現的方法),子類必須提供這些方法的具體實現。

3. 委託和事件

  • 委託(Delegate):可以看作是一個型別安全的函式指標,可以指向任何符合其簽名的方法。
  • 事件(Event):是一種特殊的多播委託,用於物件之間的通訊。一個物件可以“釋出”事件,其他物件可以“訂閱”這些事件。

4. 異常高階處理

  • 異常是程式執行時出現的錯誤。C#提供了一套機制來捕獲和處理這些錯誤。
  • 你可以使用try塊來包圍可能出錯的程式碼,catch塊來捕獲並處理特定的異常,finally塊來執行無論是否發生異常都需要執行的程式碼。

5. 反射和特性

  • 反射(Reflection):允許程式在執行時查詢和操作物件的型別資訊,比如獲取類的屬性、方法等。
  • 特性(Attribute):是一種標記,可以附加到程式碼元素上(比如類、方法),用來提供後設資料資訊。這些資訊可以在執行時透過反射讀取。

二、例項展示

1. 介面和抽象類

介面(Interface):想象一下,你經營一家餐廳,規定所有員工都必須會“服務顧客”。不論他們是廚師、服務員還是收銀員,這個“服務顧客”的能力就像是一個約定。在C#裡,我們用介面來定義這樣的約定。介面只說你“必須”做什麼,但具體怎麼做,它不管。

// 定義一個介面 ICustomerService
public interface ICustomerService
{
    // 規定所有實現這個介面的類,都必須有 ServeCustomer 這個方法
    void ServeCustomer();
}

// 廚師類實現介面
public class Chef : ICustomerService
{
    public void ServeCustomer()
    {
        Console.WriteLine("廚師微笑著給顧客上菜。");
    }
}

抽象類(Abstract Class):如果說介面是嚴格的“你必須做”,那麼抽象類就是“我給你一些基礎,你再根據需要擴充套件”。抽象類可以有具體實現的方法,也可以有抽象方法(就是隻有方法名,沒有具體實現)。

public abstract class Employee
{
    // 具體實現的方法
    public void Greet()
    {
        Console.WriteLine("歡迎光臨!");
    }

    // 抽象方法,留給子類去實現
    public abstract void DoWork();
}

public class Waiter : Employee
{
    public override void DoWork()
    {
        Console.WriteLine("服務員忙著點單。");
    }
}

2. 委託和事件

委託(Delegate):想象你在餐廳裡放了一個意見箱,任何人都可以往裡面投遞反饋,而你定期檢查並處理這些反饋。委託就像這個意見箱,它允許你定義一個方法的型別,然後你可以將多個方法像紙條一樣丟進去,最後一起或單獨呼叫它們。

public delegate void FeedbackHandler(string feedback);

public class Restaurant
{
    public event FeedbackHandler NewFeedback;

    public void ReceiveFeedback(string feedback)
    {
        Console.WriteLine($"收到反饋: {feedback}");
        
        // 如果有註冊的方法,就呼叫它們
        NewFeedback?.Invoke(feedback);
    }
}

// 使用委託
Restaurant myRestaurant = new Restaurant();
myRestaurant.NewFeedback += DisplayFeedbackOnScreen;
myRestaurant.NewFeedback += LogFeedbackToFile;

// 顯示反饋到螢幕上
private static void DisplayFeedbackOnScreen(string feedback)
{
    Console.WriteLine($"螢幕上顯示: {feedback}");
}

// 將反饋記錄到檔案
private static void LogFeedbackToFile(string feedback)
{
    // 實現日誌記錄邏輯
    Console.WriteLine($"記錄到檔案: {feedback}");
}

3. 異常高階處理

異常處理就像餐廳裡應對突發事件的預案。如果廚房突然沒煤氣了(異常),你不希望整個餐廳停止服務,而是要優雅地告訴顧客“稍等,我們正在解決”,同時後臺緊急處理問題。

try
{
    // 嘗試執行可能會出錯的程式碼,比如開啟一個不存在的檔案
    using (StreamReader file = new StreamReader("不存在的檔案.txt"))
    {
        string content = file.ReadToEnd();
    }
}
catch (FileNotFoundException)
{
    // 如果找不到檔案,友好提示使用者
    Console.WriteLine("抱歉,檔案找不到了。");
}
finally
{
    // 不管是否出錯,這裡都會執行,用於清理資源
    Console.WriteLine("操作完成,清理中...");
}

4. 反射和特性

反射(Reflection):就像擁有一面神奇的鏡子,能讓你在程式執行時檢視和操作類、屬性、方法等資訊。比如,你的餐廳需要根據選單動態調整價格,反射就能幫助你獲取選單上所有菜品的資訊,並修改它們的價格。

using System.Reflection;

public class Dish
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class Program
{
    public static void AdjustPrices(object obj)
    {
        Type type = obj.GetType();
        
        PropertyInfo[] properties = type.GetProperties();
        foreach (PropertyInfo prop in properties)
        {
            if (prop.Name == "Price")
            {
                prop.SetValue(obj, (decimal)prop.GetValue(obj) * 1.1m); // 調整價格
            }
        }
    }

    public static void Main(string[] args)
    {
        Dish spicyChicken = new Dish { Name = "辣子雞", Price = 50.0m };
        AdjustPrices(spicyChicken);
        Console.WriteLine($"調整後辣子雞的價格: {spicyChicken.Price}");
    }
}

特性(Attribute):特性就像給餐廳的菜品貼上標籤,比如“新品”、“熱銷”等。在程式碼裡,特性是一種後設資料,用來給類、方法等新增額外的資訊。

[MenuCategory("熱銷")]
public class SpicyChicken : Dish
{
    // 省略具體實現...
}

// 獲取特性
var type = typeof(SpicyChicken);
var categoryAttr = (MenuCategoryAttribute)Attribute.GetCustomAttribute(type, typeof(MenuCategoryAttribute));
Console.WriteLine($"菜品分類: {categoryAttr.Category}");

如果你直接輸入上面這段程式碼你會發現報錯缺少引用,之所以報錯,是因為MenuCategory這個特性類沒有被定義。特性(Attribute)在C#中是一個特殊的類,通常繼承自System.Attribute基類。在使用自定義特性前,你需要先定義它。下面來補充完整這部分內容:

首先,定義一個名為MenuCategoryAttribute的特性類。這個類需要繼承自System.Attribute,並且你可以為其定義屬性,比如這裡的Category

using System;

// 定義MenuCategory特性類
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] // 控制特性使用的範圍和是否可重複
public class MenuCategoryAttribute : Attribute
{
    public string Category { get; }

    public MenuCategoryAttribute(string category)
    {
        Category = category;
    }
}

接下來,你之前的程式碼就可以正常工作了,因為現在MenuCategory特性已經被正確定義了。

[MenuCategory("熱銷")] // 現在這部分應該不會報錯了
public class SpicyChicken : Dish
{
    // 省略具體實現...
}

// 獲取特性
var type = typeof(SpicyChicken);
var categoryAttr = (MenuCategoryAttribute)Attribute.GetCustomAttribute(type, typeof(MenuCategoryAttribute));

if (categoryAttr != null)
{
    Console.WriteLine($"菜品分類: {categoryAttr.Category}");
}
else
{
    Console.WriteLine("未找到MenuCategory特性");
}

記得包含對自定義特性類所在名稱空間的引用,如果特性類不在同一名稱空間下的話。這樣,你就能成功地定義和使用自定義特性了。

三、總結

  1. 介面(Interface)

    • 定義了一組方法簽名,任何實現該介面的類都需要提供這些方法的具體實現。
    • 用於確保不同類間的一致性或共享特定功能集的約定。
    • 介面中所有成員預設為公有,且只能定義方法、屬性、索引器和事件。
  2. 抽象類(Abstract Class)

    • 可以包含抽象方法(無具體實現)和具體實現的方法。
    • 不能直接例項化,用於作為其他類的基類,強制子類實現特定方法。
    • 提供了一定程度的程式碼複用和結構規劃。
  3. 委託(Delegate)

    • 類似於函式指標,用於封裝方法的引用,支援多播(繫結多個方法)。
    • 在事件處理、回撥函式等場景中廣泛使用。
    • C#中的Lambda表示式和Action、Func泛型委託進一步簡化了委託的使用。
  4. 事件(Event)

    • 基於委託的一種通訊機制,用於在類內部狀態改變時通知外部程式碼。
    • 通常包含一個add和remove訪問器,用於管理委託連結串列。
    • 事件釋出者觸發事件,事件訂閱者透過提供事件處理方法響應。
  5. 異常處理

    • 用於處理程式執行時可能出現的錯誤情況,保證程式健壯性。
    • 包括try-catch-finally結構,用於捕獲並處理異常,以及finally塊確保清理資源。
    • 可以自定義異常類,更精確地描述錯誤型別。
  6. 反射(Reflection)

    • 在執行時動態獲取型別資訊(如類名、屬性、方法等)並操作這些資訊的技術。
    • 用於實現諸如外掛系統、序列化、動態型別建立等高階功能。
    • 效能開銷相對較高,應謹慎使用。
  7. 特性(Attribute)

    • 為程式元素(如類、方法、屬性)提供後設資料的途徑。
    • 特性以[@AttributeName]形式應用,可透過反射讀取。
    • 用於標記、配置或給編譯器和執行時提供指令,增強程式碼的描述性和可操作性。

每個知識點都是C#物件導向程式設計中不可或缺的一部分,掌握它們能讓你編寫出更靈活、高效、易於維護的程式碼。

相關文章