C#委派的學習筆記_delegate_委派的定義_物件&類別層級方法引用、作為參數傳遞、委派陣列
- 委託(委派),從字面上來理解,就是將事情交由他人代為執行。
- 當自己有需求時,並不親自動手,而是請別人來完成,這就是委派的概念。
- 在 C# 中,「委派」的意義與字面上相差無幾,只不過在 C# 裡,委託與方法息息相關。
- 方法是定義在類別(Class)中的,用來實作邏輯功能,也可以理解為實際執行工作的「人」。
因此,在 C# 中可以簡單地理解為:委託就是用來呼叫方法的一種方式,而且委託與事件(Event)有密切關聯。在 .NET 框架中,定義委託的方式與定義類別相似,凡是能定義類別的地方,也都可以定義委託。
此外,適用於類別的修飾詞(如 public、private 等)也可以用於委託。委派本質上是一種安全的類型,並且會明確定義返回值型別及參數清單,這一點與方法也很相似。
委派的定義
基本語法
定義委派時需使用關鍵字 delegate,基本語法如下:
delegate 資料型別 委派名稱(參數型別 參數名稱, …);
- 使用 delegate 關鍵字來定義委派。
- 委派可以定義在類別內,也可以定義在類別外。只要是能定義類別的地方,也都能定義委託。
- 委派必須指定一個回傳資料型別,這個型別要與其綁定的方法相容。
- 委派必須命名,命名規則與一般變數或類別一致。
- 委派可以帶有參數,參數的型別與命名方式與方法的參數規則相同。
- 委派的定義只包含簽章(方法名稱、參數與回傳型別),不包含實際的程式實作內容。
在 C# 中,只要是可以定義類別(class)的地方,就可以用來定義委派。
委派(Delegate)是一種特殊的資料型別,其作用是儲存一個或多個方法的參考位址(方法指標)。
委派定義示範
//更多委派定義的示範 delegate int ProductDelegate1(int intA); delegate void ProductDelegate2(); delegate string ProductDelegate3(int intB, string strA); delegate List<int> ProductDelegate4(int[] intArray);
- 這些委託的定義都是正確的,委託可以有返回值,也可以沒有返回值;而委託的參數也可以為空,如果沒有參數,則使用空括號 () 即可。
- 你可以這樣來理解委託與方法之間的關係:當你要執行某個方法,但你不直接呼叫這個方法,而是透過委託來執行這個方法。
- 在這種情況下,你需要先將方法綁定(掛接)到委託上,這樣委託才能正確執行對應的方法。
- 掛接方法時,必須確保委託的定義與方法的定義在簽章上完全一致(即返回值與參數型別一致),否則將無法綁定。
委派的呼叫使用(物件&類別層級方法引用)
- 在使用委託之前,必須先使用 delegate 關鍵字進行定義,接著再進行實體化(建立委託實例)。
- 在實體化委託時,必須傳入一個參數,這個參數就是要綁定的方法名稱,也就是指定委託要呼叫哪一個方法。
- 傳入方法名稱時,不可以加上圓括號 (),因為你是在傳遞方法的參考,而不是呼叫它。
- 此外,該方法的回傳型別必須與委託的回傳型別一致,否則會產生編譯錯誤。
委派存取物件(實體)層級方法的示範
委派定義寫法1.將委派變數透過new 先實體化
Product.cs 類別示範
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { delegate int CountDelegate(int sum);//定義委派 internal class Product { //定義方法(副程式) //這裡所定義的方法 GetCount() 符合 CountDelegate 委託的定義要求 //因此它是可以被 CountDelegate 所引用並呼叫的。 public int GetCount(int x) { return x * 800; } } }
GetCount() 是定義在 Product 類別中的物件實體層級的方法,因此在使用該方法時,必須先建立 Product 類別的實體(Instance),才能透過該實體來呼叫 GetCount() 方法。
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { /// <summary> /// 主程式 /// </summary> internal class Program { static void Main(string[] args) { //實體化新建的Product物件 Product product = new Product(); //實體化委派並指向特定方法 CountDelegate countDelegate = new CountDelegate(product.GetCount); //調用方法 int count = countDelegate(400); Console.WriteLine(count); } } }
new出來實體化的CountDelegate委派,需傳入一個必填參數,也就是方法的名稱。
當我們在實體化委託並傳入方法時,實際上就是把方法「附加」到委託上,接著再由委託來執行該方法。也就是說,委託會代替我們去完成方法的呼叫與執行。
在呼叫委託時,我們通常會在委託變數後面加上圓括號 () 傳入參數來執行方法,這種寫法是 C# 提供的簡化語法。其實,最原始、完整的寫法是使用 Invoke() 方法來呼叫委託對應的方法。
這個概念可以用一個生活中的例子來比喻:
假設你要寄一封信給家人或朋友,你不需要親自搭火車去送信,而是將信件交給郵局(郵局就像委託),讓郵局替你完成送信的任務。
同樣地,在程式中,你不直接呼叫方法,而是透過委託來轉交這個任務,讓委託幫你執行方法,這就是委託的核心概念。
委派定義寫法2.簡化操作
在 實體化委託 時,其實可以不使用 new 關鍵字,而是直接將方法名稱(也就是方法位址)賦值給委託變數。這種省略 new 的簡化方式稱為「委託推斷(Delegate Inference)」。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { /// <summary> /// 主程式 /// </summary> internal class Program { static void Main(string[] args) { //實體化新建的Product物件 Product product = new Product(); //實體化委派並指向特定方法 //CountDelegate countDelegate = new CountDelegate(product.GetCount); CountDelegate countDelegate = product.GetCount; //調用方法 //int count = countDelegate(400); int count = countDelegate.Invoke(300); Console.WriteLine(count); } } }
Ref:
上面示範是根據物件層級方法,至於靜態方法是否也可以去透過委派引用呢?
答案是可以的
委派存取靜態方法的示範
建立一個 Student.cs 的類別檔案,並在其中新增一個委託以及一個與該委託相符的靜態方法。
GetNameById() 使用 static 修飾,是一個靜態方法。
Student.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { delegate string StudentDelegate(int id, string name); internal class Student { //靜態方法 public static string GetNameById(int id, string name) { return $"ID={id},姓名={name}"; } } }
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { /// <summary> /// 主程式 /// </summary> internal class Program { static void Main(string[] args) { StudentDelegate studentDelegate = Student.GetNameById; var stuInfo = studentDelegate(100, "王大陸"); Console.WriteLine(stuInfo); } } }
委派作為參數傳遞
由於委託可以引用方法,本質上就是引用方法的記憶體位址
而且委託本身又可以當作類別來使用,因此可以將委託物件作為參數傳遞給其他方法。
當我們將委託物件作為參數傳遞時,其實就是實現了「將方法作為參數」傳入另一個方法的目的,這也是委託最主要、最常見的用途之一。
這邊定義一個Apple.cs 類別檔案
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { delegate double AppleDelegate(int sum, double price); internal class Apple { public double Amount(int sum,double price) { return sum * price; } public static double GetAmount(AppleDelegate appleDelegate) { //藉由委派去呼叫方法 double d = appleDelegate(100, 89.60); return d; } } }
GetAmount() 方法帶有一個參數,這個參數的型別是 AppleDelegate 委託。
當呼叫 GetAmount() 方法時,所傳入的實際參數是一個已經實體化的 AppleDelegate 物件。
對於 AppleDelegate 委託而言,在實體化的過程中必須綁定一個方法,也就是說,當 AppleDelegate 的實例被傳入 GetAmount() 方法時,它其實已經帶有一個可執行的方法參考。
因此,在 GetAmount() 方法的內部,就可以透過該委託來呼叫對應的方法。
在Program.cs 主程式中去嘗試呼叫GetAmount方法:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { /// <summary> /// 主程式 /// </summary> internal class Program { static void Main(string[] args) { Apple apple = new Apple(); AppleDelegate appleDelegate = apple.Amount; double amount = Apple.GetAmount(appleDelegate); Console.WriteLine(amount); } } }
這邊可以看到委派其實就是一種方法的變數載體,在此我們把他以參數形式傳入。
委派陣列
可以將多個方法放入委託陣列(delegate array)中,這樣就能在同一個地方集中處理多個方法的呼叫。這種做法可以讓你透過迴圈或統一邏輯,一次性地對多個方法進行調用,使程式結構更清晰、可維護性更高。
這在需要對一組具有相同簽章的方法進行批次操作時特別實用。
建立Flower.cs類別程式
在該類中創建 1 個委託和 2 個方法
然後再在 Flower 類中創建一個 Call() 方法,並在該方法內定義一個委派陣列
委託也是一種類型,可以作為陣列的類型來儲存更多的委託實例。
此代碼中,totalDelegates 委託陣列儲存了 2 個委託實例,並且都攜帶了方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { internal class Flower { public delegate double TotalDelegate(int sum, double price); public double SingleTotal(int sum, double price) { return sum * price; } public double DoubleTotal(int sum, double price) { return 2 * (sum * price); } public void Call() { TotalDelegate[] totalDelegates = { new TotalDelegate(SingleTotal), new TotalDelegate(DoubleTotal) }; foreach (var total in totalDelegates) { double d = total(100, 3.99); Console.WriteLine(d); } } } }
對於委託陣列,透過 foreach 迴圈陣列中的每個方法,統一進行調用,從而處理方法中的資料。
Program.cs主程式去實體化Flower物件後再呼叫Call方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoCourse { /// <summary> /// 主程式 /// </summary> internal class Program { static void Main(string[] args) { Flower flower = new Flower(); flower.Call(); } } }
留言
張貼留言