標籤:委託 delegate
看C#進階編程第八版看到委託,有人說它和Java中的代理有些相似,但我認為這是一個C#與其他程式設計語言不同的地方,這也應該很重要,不然書上也不會大篇幅的講委託的概念和應用。我在網上查閱了關於委託的資料,發現它們就像是一道檻兒,過了這個檻的人,覺得真是太容易了,而沒有過去的人每次見到委託和事件就覺得心裡憋得慌,混身不自在。
那初次看見“委託”這個詞,我腦海中出現的第一個疑問就是:
1.什麼是委託?
我看完整這個章節,依然是一知半解的,功夫不負有心人,通過強大的網路,我尋找到了答案,那到底什麼是委託呢?
委託就是可以把方法作為參數傳遞,也就是說通過委託可以像操作變數一樣的操作方法參數。看到這裡我發現這並不是C#首創的這個概念,因為在C++中就有函數指標的概念和應用,我理解這其實就是微軟把C++中的函數指標作為原型而衍生出來的,經過封裝的這麼一個特殊概念,其實現在我看來也不是很特別嗎,畢竟在C++中曾經學過呀。
現在我已經知道了委託的概念了,那麼我第二個問題又來了。
2.這個委託怎麼用呢?
我覺得怎麼用,還是要拿Demo來進行講解會比較方便理解和對比。下面是我參看網路上的大神們所用到代碼,我們不討論所給Demo的代碼是否有實際意義,只是為了讓讀者能夠更加方便的理解什麼叫委託。代碼如下:
a.現在要模仿人打招呼。那麼我們需要知道他的名字方法如下:
public static void ChineseGreeting(string name){ Console.WriteLine("早上好!"+name); }static void Main(string[] args) { ChineseGreeting("李雷"); Console.ReadKey(); }
b.要是現在要給一個老外(不懂中文的)打招呼,那要這怎麼辦呢?是不是應該知道給老外說英文呢?單獨建立一個方法呢?不過在建立方法之前,我們需要先用一個枚舉類型去判讀是外國人還是中國人。
代碼如下:
public enum Language{ Chinese,English }public void EnglishGreeting(string name){ Console.WriteLine("Morning !"+name); } public GreatPeople(string name,Language language){ switch(language) { case language.English:EnglishGreeting(name);break; case language.Chinese:ChineseGreeting(name);break; } }
只要有些編程經驗的人就知道,上面的解決方案不好,如果下次有日本人,有韓國人,非洲人等等,那麼我們是不是就又要去添加枚舉元素,添加問候方法!上面的方法不利於擴充。所以我們就想有沒有什麼可以不用這麼麻煩而且能很好的擴充呢?我現在分析上面的解決方案是如何做到的。
首先添加一個對應語言的問候的方法是不可少的,枚舉元素也是必須的,枚舉元素是為了判斷調用哪個語言的問候方法去問候的標誌,那麼既然我們前面提到了方法可以做參數那為什麼不直接傳方法名稱呢?下面就是利用代理實現上面方案的代碼如下:
public delegate void GreetingDelegate(string name); class Program { public static void EnglishGreeting(string name) { Console.WriteLine("Morning!"+name); } public static void ChineseGreeting(string name) { Console.WriteLine("早上好!" + name); } //這是一個 public static void GreetingPeople(string name, GreetingDelegate greetingmethod) { greetingmethod(name);// } static void Main(string[] args) { GreetingPeople("Mike", EnglishGreeting); GreetingPeople("李雷", ChineseGreeting); Console.ReadKey(); } }
看上面的代碼,是不是感覺舒服多了我們不用使用枚舉了,也不用進行選擇(switch)了。
請注意:public static void GreetingPeople(string name, GreetingDelegate greetingmethod)這個方法中使用了一個委託類型的參數傳遞使用的問候方法。是不是感覺很神奇。
看到上面的Demo你是不是對委託有些感覺了呢?現在我的問題又來了。
3.方法是如何綁定到委託的?
這個問題我也需要借用上面的Demo進行修改一下,以便於顯示效果。Demo代碼如下:
public delegate void GreetingDelegate(string name); class Program { public static void EnglishGreeting(string name) { Console.WriteLine("Morning,"+name); } public static void ChineseGreeting(string name) { Console.WriteLine("早上好!" + name); } public static void GreetingPeople(string name, GreetingDelegate greetingmethod) { Console.WriteLine("問候開始..."); greetingmethod(name); Console.WriteLine("問候結束!"); } static void Main(string[] args) { GreetingDelegate delegate1;//委託執行個體 delegate1 = EnglishGreeting; delegate1 += ChineseGreeting; GreetingPeople("Mike", EnglishGreeting); GreetingPeople("李雷", ChineseGreeting); GreetingPeople("John", delegate1); Console.ReadKey(); } }
結果如下:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/4C/D1/wKiom1RFytiy5BwmAAB0Uhk_LI8973.jpg" title="1.PNG" alt="wKiom1RFytiy5BwmAAB0Uhk_LI8973.jpg" />
多個方法可以被綁定到一個委託執行個體中去。
注釋:delegate1 = EnglishGreeting;這個操作符"="是初始化委託執行個體,那麼"+="是綁定文法。那麼到底
初始化和綁定還可以這樣做:
GreetingDelegate delegate1; delegate1 = EnglishGreeting; delegate1 += ChineseGreeting; GreetingPeople("John", delegate1); GreetingDelegate delegate2 = new GreetingDelegate(EnglishGreeting); delegate2 += ChineseGreeting; GreetingPeople("LiLy", delegate2); Console.ReadKey();
這樣的看,委託是不是和類非常的相似!你先下面這段代碼和資訊你會發現越發的像。
GreetingDelegate delegate3 = new GreetingDelegate();//報錯,不含有0個參數的建構函式 delegate3 += EnglishGreeting; Console.ReadKey();
說明編譯器在編譯的時候會把委託當做類進行編譯。這一點也是函數指標比不了的。
總結:
GreetingDelegate
1.委託定義
委託的聲明原型是
delegate <函數傳回型別> <委託名> (<函數參數>)
例子:public delegate void GreetingDelegate(string name);//定義了一個委託它可以註冊返回void類型且有一個int作為參數的函數這樣就定義了一個委託,但是委託在.net內相當於聲明了一個類,類如果不執行個體化為對象,很多功能是沒有辦法使用的,委託也是如此.
2.委託執行個體化
委託執行個體化的原型是
<委託類型> <執行個體化名>=new <委託類型>(<註冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//用函數CheckMod執行個體化上面的CheckDelegate 委託為_checkDelegate
在.net 2.0開始可以直接用匹配的函數執行個體化委託:
<委託類型> <執行個體化名>=<註冊函數>
例子:CheckDelegate _checkDelegate=CheckMod;//用函數CheckMod執行個體化上面的CheckDelegate 委託為_checkDelegate現在我們就可以像使用函數一樣來使用委託了,在上面的例子中現在執行_checkDelegate()就等同於執行CheckMod(),最關鍵的是現在函數CheckMod相當於放在了變數當中,它可以傳遞給其它的CheckDelegate引用對象,而且可以作為函數參數傳遞到其他函數內,也可以作為函數的傳回型別
3.用匿名函數初始化委託
上面為了初始化委託要定義一個函數是不是感覺有點麻煩,另外被賦予委託的函數一般都是通過委託執行個體來調用,很少會直接調用函數本身。
在.net 2.0的時候考慮到這種情況,於是匿名函數就誕生了,由於匿名函數沒有名字所以必須要用一個委託執行個體來引用它,定義匿名函數就是為了初始化委託
匿名函數初始化委託的原型:
<委託類型> <執行個體化名>=new <委託類型>(delegate(<函數參數>){函數體});
當然在.net 2.0後可以用:
<委託類型> <執行個體化名>=delegate(<函數參數>){函數體};
delegate void Func1(int i); delegate int Func2(int i); static Func1 t1 =new Func1(delegate(int i) { Console.WriteLine(i); }); static Func2 t2; static void Main(string[] args) { t2 = delegate(int j) { return j; }; t1(2); Console.WriteLine(t2(1)); }
當然在.net 3.0的時候又有了比匿名函數更方便的東西lambda運算式,這兒就不說了。
4.泛型委派
委託也支援泛型的使用
泛型委派原型:
delegate <T1> <委託名><T1,T2,T3...> (T1 t1,T2 t2,T3 t3...)
例子:
delegate T2 A<T1,T2>(T1 t);//定義有兩個泛型(T1,T2)的委託,T2作為委託函數傳回型別,T1 作為委託函數參數類型
static int test(int t)
{
return t;
}
static void Main(string[] args)
{
A<int, int> a =test;//將泛型委派委託<T1,T2>執行個體化為<int,int>,即表示有一個int類型 參數且傳回型別是int的函數,所以將test用來執行個體化委託
Console.WriteLine(a(5));//輸出5
}
5.委託的多播性
在上面執行個體化委託的時候看到:必須將一個匹配函數註冊到委託上來執行個體化一個委派物件,但是一個執行個體化委託不僅可以註冊一個函數還可以註冊多個函數,註冊多個函數後,在執行委託的時候會根據註冊函數的註冊先後順序依次執行每一個註冊函數
函數註冊委託的原型:
<委託類型> <執行個體化名>+=new <委託類型>(<註冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//將函數CheckMod註冊到委託執行個體_checkDelegate上
在.net 2.0開始可以直接將匹配的函數註冊到執行個體化委託:
<委託類型> <執行個體化名>+=<註冊函數>
例子:CheckDelegate _checkDelegate+=CheckMod;//將函數CheckMod註冊到委託執行個體_checkDelegate上
之後我們還可以註冊多個函數到委託上:
例子:_checkDelegate+=CheckPositive;//將函數CheckPositive註冊到委託執行個體_checkDelegate上
_checkDelegate();//執行這個委託執行個體會先執行CheckMod()再執行CheckPositive()
實際上使用+=符號的時候會判斷
如果此時委託還沒有執行個體化(委託執行個體為null),它會自動用+=右邊的函數執行個體化委託
如果此時委託已經執行個體化,它會只把+=右邊的函數註冊到委託執行個體上
另外有一點需要注意的是,如果對註冊了函數的委託執行個體從新使用=號賦值,相當於是重新執行個體化了委託,之前在上面註冊的函數和委託執行個體之間也不再產生任何關係,後面的例子會講到這點!
當然有+=註冊函數到委託,也有-=解除註冊
例子:_checkDelegate-=new CheckDelegate(CheckPositive);//解除CheckPositive對_checkDelegate的註冊
_checkDelegate-=CheckPositive;//.net 2.0開始可以用這種方式解除註冊
本文出自 “愛咖啡” 部落格,請務必保留此出處http://4837471.blog.51cto.com/4827471/1566221
C#進階編程之委託