對c#中委託的理解

來源:互聯網
上載者:User

理解委託從一個簡單的例子開始

金城武演的有部老電影叫《薰衣草》,裡面有個情節大概是這樣的:小金收客戶的錢,然後代表客戶去向不同的人Say I love you。

一開始他的客戶都是中國人,只需要說中文,如下程式碼範例,很簡單,支援所有中國客戶:

複製代碼 代碼如下:public class LoveManager
{
public void Love(string name)
{
Console.WriteLine("我愛你, {0}", name);
}
}

複製代碼 代碼如下:class Program
{
static void Main(string[] args)
{
LoveManager loveManager = new LoveManager();
loveManager.Love("張曼玉");
}
}

執行結果:

複製代碼 代碼如下:我愛你, 張曼玉

我留意到後來電影裡出現了外國客戶,我想代碼應該是這樣:

複製代碼 代碼如下://枚舉,可擴充多語種
public enum Language
{
English,
Chinese
}

複製代碼 代碼如下:public class LoveManager
{
public void Love(string name, Language lang)
{
switch (lang)
{
case Language.Chinese:
loveChinese(name);
break;
case Language.English:
loveEnglish(name);
break;
}
}

//漢語客戶專用
public void LoveChinese(string name)
{
Console.WriteLine("我愛你, {0}", name);
}

//英語客戶專用
public void LoveEnglish(string name)
{
Console.WriteLine("I love you, {0}", name);
}
}

複製代碼 代碼如下:class Program
{
static void Main(string[] args)
{
LoveManager loveManager = new LoveManager();
loveManager.Love("張曼玉", Language.Chinese);
loveManager.Love("Sophie Marceau", Language.English);
}
}

執行結果:

複製代碼 代碼如下:我愛你, 張曼玉
I love you, Sophie Marceau

OK,現在張曼玉能聽懂“我愛你”,Sophie Marceau能聽懂“I love you”。雖然支援了英漢雙語表白,但以後還有法國客戶,葡萄牙客戶,阿拉伯客戶怎麼辦?每擴充一個語種除了添加這個語種“我愛你”的方法,還得擴充枚舉,擴充LoveManager.Love(),確實有些繁瑣。

C語言時代:指標

此時,不得不提到C語言中大名鼎鼎的指標。指標允許把一個函數的地址作為參數傳遞給另一個函數,這個特性在以後的各種進階語言中得到了擴充和加強。先看如下C代碼:

複製代碼 代碼如下:#include <stdio.h>

//接受一個指標類型的參數
void func1(void(*p)(void)){
printf("this is func1\r\n");
//通過指標調用函數
p();
}

void func2(){
printf("this is func2\r\n");
}

int main() {
//將func2地址作為參數傳遞
func1(func2);
return 0;
}

執行結果:

複製代碼 代碼如下:this is func1
this is func2

在.Net中能不能像C語言一樣,把函數作為一個參數傳遞並且調用呢?

複製代碼 代碼如下://這段代碼並不能被執行,但如果在.Net中可以這樣寫的話問題就會簡單很多 Love("張曼玉", LoveChinese);
Love("Sophie Marceau", LoveEnglish);

.Net中更完美的解決方案:委託

在.Net中不但可以像C語言一樣將函數作為參數傳遞,並且.Net提供了型別安全機制和更加強大的功能,如下提供了使用委託的完整程式碼範例:

複製代碼 代碼如下:using System;

namespace DelegateDemo
{
//定義委託
public delegate void LoveDelegate(string name);

public class LoveManager
{
public void Love(string name, LoveDelegate loveDelegate)
{
loveDelegate(name);
}

//漢語客戶專用
public void LoveChinese(string name)
{
Console.WriteLine("我愛你, {0}", name);
}

//英語客戶專用
public void LoveEnglish(string name)
{
Console.WriteLine("I love you, {0}", name);
}
}

class Program
{
static void Main(string[] args)
{
LoveManager loveManager = new LoveManager();
loveManager.Love("張曼玉", loveManager.LoveChinese);
loveManager.Love("Sophie Marceau", loveManager.LoveEnglish);
}
}
}

執行結果:

複製代碼 代碼如下:我愛你, 張曼玉
I love you, Sophie Marceau

定義委託

複製代碼 代碼如下:public delegate void LoveDelegate(string name);

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

在C#中委託使用特有的關鍵字 delegate 來定義,在delegate之後緊跟的是函數簽名。為了確保型別安全,.Net中的委託要求函數具有相同的簽名,比如 func(int p) 和func(string p)不能使用同一個委託,因為它們的參數類型不一樣。

通過ILDasm.exe可以發現,定義委託的那行代碼實際在編譯時間會自動產生一個類,如果要還原這個類,代碼會是這樣:

複製代碼 代碼如下:public class LoveDelegate : System.MulticastDelegate
{
//構造器
public LoveDelegate(Object obj, IntPtr method);

//原型
public virtual void Invoke(string name);

//非同步回調
public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object obj);
public virtual void EndInvoke(IAsyncResult result);
}

因此,委託實際上就是一個類,它繼承至System.MulticastDelegate,凡是可以定義類的地方,都可以定義委託。

委託的建構函式

複製代碼 代碼如下:LoveManager loveManager = new LoveManager();
//編譯不能通過,委託必須使用帶有一個參數的建構函式
//LoveDelegate loveDelegate = new LoveDelegate();
LoveDelegate loveDelegate = new LoveDelegate(loveManager.LoveChinese);
loveDelegate("吳劍");

與類不同的是,委託必須使用帶有一個參數的建構函式。

委託推斷文法

複製代碼 代碼如下:LoveManager loveManager = new LoveManager();
//等同於:LoveDelegate loveDelegate = new LoveDelegate(loveManager.LoveChinese);
LoveDelegate loveDelegate = loveManager.LoveChinese;
loveDelegate("吳劍");

委託與方法進行綁定

回到上面的例子,有一天一富二代找到小金,說錢不是問題,你去張曼玉樓下,用中文喊一遍,再用英文喊一遍。

複製代碼 代碼如下:static void Main(string[] args)
{
LoveManager loveManager = new LoveManager();
//定義委託變數
LoveDelegate delegate1;
//變數初始化(用中文喊一遍)
delegate1 = loveManager.LoveChinese;
//Binder 方法(用英文再喊一遍)
delegate1 += loveManager.LoveEnglish;
delegate1("張曼玉");
}

執行結果:

複製代碼 代碼如下:我愛你, 張曼玉
I love you, 張曼玉

我們可以用 += 將多個方法綁定到一個委託,也可以使用 -= 移除方法與委託的綁定。

匿名方法

客戶的需求總是千變萬化,一個客戶跟小金說,我要跟曼玉表白,除了用中英文,能不能後面再給我加一句,曼玉一聽到這句準會答應我。

複製代碼 代碼如下:LoveManager loveManager = new LoveManager();
LoveDelegate loveDelegate = loveManager.LoveEnglish;
loveDelegate += loveManager.LoveChinese;
loveDelegate += delegate(string name)
{
Console.WriteLine("{0}, 還記得大明湖畔的夏雨荷嗎?", name);
};
loveDelegate("曼玉");

執行結果:

複製代碼 代碼如下:I love you, 曼玉
我愛你,曼玉
曼玉,還記得大明湖畔的夏雨荷嗎?

針對這位特殊客戶使用了匿名方法,不是每個人示愛的時候都會提到大明湖畔的夏雨荷,也就是這位特殊客戶使用一次而以,所以沒有必要定義一個獨立的方法。使用匿名方法可以減少編碼量,降低代碼複雜度。

Lambda(λ)運算式

C# 3.0為匿名方法提供了Lambda運算式,如下代碼執行結果與上面的樣本完全一致:

複製代碼 代碼如下:LoveManager loveManager = new LoveManager();
LoveDelegate loveDelegate = loveManager.LoveEnglish;
loveDelegate += loveManager.LoveChinese;
//用紅色字型標出了Lambda運算式部分loveDelegate += name =>
{
Console.WriteLine("{0}, 還記得大明湖畔的夏雨荷嗎?", name);
};
loveDelegate("曼玉");

=>為Lambda運算子,運算子左邊列出匿名方法需要的參數,可以這樣使用:

(string param1, int param2)

也可以:

(param1, param2)

如範例程式碼只有一個參數還可以去掉括弧:

param1

Lambda運算式右邊為匿名方法實現代碼,如果實現代碼只有一行,還可以刪除花括弧和return語句,因為編譯器會自動添加。

共同學習,共同進步!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.