C# 委托(Delegate)


什么是委托

委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞,這種將方法動態地賦給參數的做法,可以避免在程序中大量使用If-Else(Switch)語句,同時使得程序具有更好的可擴展性。

委托是一種動態調用方法的類型,屬於引用型。

委托是對方法的抽象和封裝。委托對象實質上代表了方法的引用(即內存地址)

可以理解為函數的一個包裝,它使得c#中的函數可以作為參數來被傳遞

簡單理解是這樣的

比如您要管您的孩子,把孩子送進了幼兒園

OK,此時您就把您的孩子委托給了幼兒園

當幼兒園放學,將孩子交還給您的手中則是委托的回調

當然我這里的例子是說異步委托調用,您也可以同步

什么是同步委托調用?

您去銀行存錢,把錢給櫃員,他們幫你點鈔,存款然后給您存折或卡

那么您就相當於把錢委托給了櫃員讓他去執行存錢的這個函數

委托的定義

1. 以delegate關鍵字開頭。

2. 返回類型+委托類型名+參數列表

public delegate void MyDelegate(int para1, string para2);

委托能包裝的方法是有一定限制的,例如能被前面的委托類型MyDelegate包裝的方法需要滿足以下條件:

    1.方法的返回類型必須為void;

    2.方法必須有兩個參數,並且第一個參數應為int類型,第二個參數為string類型。

總結:可以被委托包裝的方法必須滿足以下規則:

    1.方法的簽名必須與委托一致,方法簽名包括參數的個數、類型和順序;

    2.方法的返回類型要和委托一致,注意,方法的返回類型不屬於方法簽名的一部分。

 委托的使用

//委托使用的演示
    class Program { //1.使用delegate關鍵字來定義一個委托類型
        public delegate void MyDelegate(int para1, int para2); static void Main(string[] args) { //2.聲明委托變量d
 MyDelegate d; //3.實例化委托類型,傳遞的方法也可以為靜態方法,這里傳遞的是實例方法
             d = new MyDelegate(new Program().Add); //4.委托類型作為參數傳遞給另一個方法
 MyMethod(d); Console.ReadKey(); } void Add(int para1,int para2) { int sum = para1 + para2; Console.WriteLine("兩個數的和為:" + sum); } private static void MyMethod(MyDelegate mydelegate) { //5.在方法中調用委托
             mydelegate(1, 2); } }

 從以上代碼可以看出,使用委托的步驟為:定義委托類型—聲明委托變量—實例化委托—作為參數傳遞給方法—調用委托。如下具體分析委托的使用過程。

  (1)定義委托類型:public delegate void MyDelegate(int para1, int para2) 其定義方式類似於方法的定義,只是多了一個delegate關鍵字。

  (2)聲明委托變量:MyDelegate d;既然委托是一種類型,那么可以使用委托來聲明一個委托變量,相當於int a;

  (3)實例化委托: d = new MyDelegate(new Program().Add);。第二步只是聲明了委托變量,但並沒有將它實例化。類的實例化使用new關鍵字來實現,而委托也屬於類類型,所以委托的實例化也使用new關鍵字來進行的。這里需要注意的是,委托的實例化是用一個方法名(不能帶左右括號)作為參數,並且該方法的定義必須符合委托的定義,即該方法的返回類型、參數個數和類型必須與委托定義中的一樣。這樣,前面3步就好比構造了一個律師對象,而方法InstanceMethod好比是當事人的方法。

  (4)作為參數傳遞給方法:MyMethod(d);。委托使用得在C#中,可以把一個方法作為另一個方法的參數,而委托可以看作是一個包裝方法的對象。

  (5)在方法中調用委托:MyMethod(d);。委托使用得在c#中,可以把一個方法作為另一個方法的參數,而委托可以看作是一個包裝方法的對象。

   總結:在使用委托時,需要注意以下幾個問題。

    1.在第三步中,被傳遞的方法的定義必須與委托定義相同,即方法的返回類型和參數個數、參數類型都必須與委托相同。並且,傳遞的是方法名,方法名后不能帶有左右括號。

    2.在第五步中,委托的調用與方法調用類似,傳遞的實參類型和個數必須與委托定義一致。

    3.由於委托是方法的包裝類型,所以對委托的調用也就是對其所包裝的的方法的調用,上面第5步時間上是調用了Add方法來對傳入的實參進行計算。

為什么要使用委托

委托使得一個方法可以作為另一個方法的參數進行傳遞,這就是委托最大的作用。使用委托可以將同類型的方法綁定到同一個變量上,當調用此變量時就可以一次調用綁定的方法,很方便。如下例子:

例如我們要實現一個打招呼的方法,而每個國家打招呼的方式都不一樣,剛開始我們可能會像下面這樣實現打招呼的方法:

public void Greeting(string name,string language) { switch (language) { case "zh-cn": ChineseGreeting(name); break; case "en-us": EnglishGreeting(name); break; default: EnglishGreeting(name); break; } } public void EnglishGreeting(string name ) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); }

若后續我們需要添加德國、日本等打招呼方法,就必須修改Greeting方法內的case語句,來適應新的需求,這樣特別不方便。有了委托,我們就可以把函數作為參數,並像如下代碼實現Greeting方法:

public delegate void GreetingDelegate(string name); static void Main(string[] args) { //引入委托
             Program p = new Program(); p.Greeting("小葉", p.ChineseGreeting); p.Greeting("Tommy Li", p.EnglishGreeting); Console.Read(); } public void Greeting(string name,GreetingDelegate callback) { callback(name); } public void EnglishGreeting(string name) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); }

如下,增加德國人打招呼:

class Program { public delegate void GreetingDelegate(string name); static void Main(string[] args) { //引入委托
             Program p = new Program();
p.Greeting(
"小葉", p.ChineseGreeting); p.Greeting("Tommy Li", p.EnglishGreeting); p.Greeting("Ha Wa", p.GermanyGreeting); Console.Read(); } public void Greeting(string name,GreetingDelegate callback) { callback(name); } public void EnglishGreeting(string name) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); } public void GermanyGreeting(string name) { Console.WriteLine("Hallo, " + name); } }

委托鏈的使用

委托鏈其實就是委托類型,只是委托鏈把多個委托鏈接在一起而已,也就是說,我們把鏈接了多個方法的委托稱為委托鏈或多路廣播委托。如下:

public delegate void DelegateTest(); static void Main(string[] args) { //用靜態方法來實例化委托
             DelegateTest dtstatic = new DelegateTest(Program.method1); DelegateTest dtinstance = new DelegateTest(new Program().method2); //定義一個委托對象,一開始初始化為null,即不代表任何方法。
             DelegateTest delegatechain = null; //使用 “+”符號鏈接委托,鏈接多個委托后就成為了委托鏈 delegatechain += dtstatic; delegatechain += dtinstance; //調用委托鏈
 delegatechain(); Console.Read(); }
private static void method1() { Console.WriteLine("這是靜態方法"); } //實例方法  private void method2() { Console.WriteLine("這是實例方法"); }

從委托鏈中移除委托

public delegate void DelegateTest(); static void Main(string[] args) { //用靜態方法來實例化委托
             DelegateTest dtstatic = new DelegateTest(Program.method1); DelegateTest dtinstance = new DelegateTest(new Program().method2); //定義一個委托對象,一開始初始化為null,即不代表任何方法。
             DelegateTest delegatechain = null; //使用 “+”符號鏈接委托,鏈接多個委托后就成為了委托鏈 delegatechain += dtstatic; delegatechain += dtinstance; //使用 “-”運算符 移除委托 
             delegatechain -= dtstatic; //調用委托鏈
 delegatechain(); Console.Read(); } private static void method1() { Console.WriteLine("這是靜態方法"); } //實例方法 
         private void method2() { Console.WriteLine("這是實例方法"); }
使用委托可以將多個方法綁定到同一個委托變量,當調用此變量時(這里用“調用”這個詞,是因為此變量代表一個方法),可以依次調用所有綁定的方法。

多播委托

如果要調用多個方法,就需要多次顯式調用這個委托。但是委托也可以包含多個方法,這種委托稱為多播委托。

如果調用多播委托就可以按順序連續調用多個方法。為此,委托的簽名就必須返回void;否則,就只能得到委托調用的最后一個方法的結果。

例如:

  • Action operations=方法一;operartion+=方法二;

  • 也可以 委托1=方法1;委托二=方法2;委托3=委托1+委托2。

所以當調用委托3時,委托1和委托2會同時執行,與第一種實現多播委托一個道理。

但是多播委托還有一個缺陷,一旦一個委托發生異常,其他委托都會停止。為了避免這個問題,應自己迭代方法列表。Delegate類定義GetInvocationList()方法,它返回一個Delegate對象數組。現在可以使用這個委托調用與委托直接相關的

方法,捕獲異常,並繼續下一次迭代。(.net core 中間件)

匿名方法

到目前為止,要想使委托工作,方法名必須已經存在。但還有另外一種使用委托的方式:匿名方法。

匿名方法(Anonymous methods) 提供了一種傳遞代碼塊作為委托參數的技術。匿名方法是沒有名稱只有主體的方法。

在匿名方法中您不需要指定返回類型,它是從方法主體內的 return 語句推斷的。

編寫匿名方法的語法

匿名方法是通過使用 delegate 關鍵字創建委托實例來聲明的。例如:

delegate void NumberChanger(int n); ... NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); };

代碼塊 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主體。

委托可以通過匿名方法調用,也可以通過命名方法調用,即,通過向委托對象傳遞方法參數。

例如:

nc(10);

實例

下面的實例演示了匿名方法的概念:

using System; delegate void NumberChanger(int n); namespace DelegateAppl { class TestDelegate { static int num = 10; public static void AddNum(int p) { num += p; Console.WriteLine("Named Method: {0}", num); } public static void MultNum(int q) { num *= q; Console.WriteLine("Named Method: {0}", num); } static void Main(string[] args) { // 使用匿名方法創建委托實例
            NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); }; // 使用匿名方法調用委托
            nc(10); // 使用命名方法實例化委托
            nc =  new NumberChanger(AddNum); // 使用命名方法調用委托
            nc(5); // 使用另一個命名方法實例化委托
            nc =  new NumberChanger(MultNum); // 使用命名方法調用委托
            nc(2); Console.ReadKey(); } } }
View Code

當上面的代碼被編譯和執行時,它會產生下列結果:

匿名方法是用作委托的參數的一段代碼,用匿名方法定義委托的語法與前面的定義並沒有區別,但在實例化委托時,就有了區別。下面是一個非常簡單的代碼,它說明了如何使用匿名方法:

Func<string,string> anonDel=delegate(string param) {   param+=mid;   param+=" and this was added to the string.";   return param; }

使用匿名方法的規則很明顯,它前面是關鍵字delegate,后面是一個字符串參數.

使用匿名方法

  1. 聲明委托變量時候作為初始化表達式。
  2. 組合委托時在賦值語句的右邊。
  3. 為委托增加事件時在賦值語句的右邊。

匿名方法語法

delegate (parameters ){implementationcode}; 關鍵字   參數        方法體

匿名方法不會聲明返回值類型。但是匿名方法返回值類型必須和委托返回值一樣。

我們可以使圓括號為空,或省略圓括號來簡化匿名方法的參數列表。但是僅在下面兩項都為真的情況下才可以這么做。

  1. 委托的參數列表不包含任何out參數的委托。
  2. 匿名方法不使用任何參數。
例如:
 class Program { delegate int otherdel(int param); public static void Main() { otherdel del = delegate { cleanup(); printMessage(); }; } } 

params參數

如果委托參數包含params參數,那么params關鍵字就會被匿名方法的參數列表忽略。如下:

delegate int otherdel(int x,params int y); otherdel del = delegate(int x,int y) { };

 

 

 

 

 

參考鏈接:https://www.cnblogs.com/xiaoyehack/tag/c%23%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/default.html?page=1

參考鏈接:https://www.cnblogs.com/zhan520g/p/10917053.html

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM