原文地址:http://www.cnblogs.com/qingyuan/archive/2010/05/11/1732415.html 1.什麼是委托,為什麼要使用委托 我正在埋頭苦寫程式,突然想喝水,但是又不想自己去掉杯水而打斷自己的思路,於是我就想讓女朋友去給我倒水。她去給我倒水,首先我得讓她 ...
原文地址:http://www.cnblogs.com/qingyuan/archive/2010/05/11/1732415.html
1.什麼是委托,為什麼要使用委托
我正在埋頭苦寫程式,突然想喝水,但是又不想自己去掉杯水而打斷自己的思路,於是我就想讓女朋友去給我倒水。她去給我倒水,首先我得讓她知道我想讓她乾什麼,通知她之後我可以繼續寫自己的程式,而倒水的工作就交給了她。這樣的過程就相當於一個委托。
在程式過程中,當程式正在處理某個事件的時候,我需要另外的程式代碼去輔助處理一些事情,於是委托另一個程式模塊去處理,而委托就可以達到這種目的,我可以利用委托通知另外的程式模塊,該去調用哪個函數方法。委托其實就起到了這樣一個作用,將函數簽名傳遞到了另一個函數中。或許這樣講還是有些模糊,看看後面的具體實例。
2.委托的定義
delegate int Add(int num1,int num2);
delegate void ConvertNum(string result);
上面是定義兩個委托的例子,其實很簡單。聲明一個委托使用delegate關鍵字,上面分別是定義的帶返回值的委托和不帶返回值的委托,
兩個委托都有傳遞參數,當然也可以不傳遞參數。其實委托也是一個類,委托派生為System.MulticastDelegate,而System.MulticastDelegate
又繼承System.Delegate,如果你知道這個也就明白委托其實是一個特殊的類。
1 public delegate string TeaDelegate(string spText); 2 3 public class DelegateSource 4 { 5 public void TestDelegate() 6 { 7 Operator op = new Operator(); 8 TeaDelegate tea = new TeaDelegate(op.GetTea); 9 Console.WriteLine("去給我倒杯水"); 10 Console.WriteLine(); 11 string result=tea("去給我倒杯水"); 12 Thread.Sleep(5000); 13 Console.WriteLine(result); 14 Console.WriteLine(); 15 } 16 } 17 18 public class Operator 19 { 20 /// <summary> 21 /// 確定是否還有水 22 /// </summary> 23 private bool flag = true; 24 25 public string GetTea(string spText) 26 { 27 if (spText == "去給我倒杯水") 28 { 29 if (flag) 30 { 31 return "老公,茶來了"; 32 } 33 else 34 { 35 return "老公,沒有水了"; 36 } 37 } 38 return "等待......."; 39 } 40 }View Code
輸出結果
上面使用最普通的一種方式來定義了一個委托的使用,這個例子雖然很簡單,但是能夠很形象的描述委托的使用。
3.委托的三種形式
(1).推斷
1 public delegate string TeaDelegate(string spText); 2 3 public class DelegateSource 4 { 5 public void TestDelegate() 6 { 7 Operator op = new Operator(); 8 TeaDelegate tea = op.GetTea; 9 Console.WriteLine("去給我倒杯水"); 10 Console.WriteLine(); 11 string result=tea("去給我倒杯水"); 12 Thread.Sleep(5000); 13 Console.WriteLine(result); 14 Console.WriteLine(); 15 } 16 } 17 18 public class Operator 19 { 20 /// <summary> 21 /// 確定是否還有水 22 /// </summary> 23 private bool flag = true; 24 25 public string GetTea(string spText) 26 { 27 if (spText == "去給我倒杯水") 28 { 29 if (flag) 30 { 31 return "老公,茶來了"; 32 } 33 else 34 { 35 return "老公,沒有水了"; 36 } 37 } 38 return "等待......."; 39 } 40 }View Code
在委托定義的例子中我們看到委托的使用方法是在委托實例化的時候指定的[new DelegateName(FunctionName)],這裡可能表述不是太但是代碼應該看得白了。 而委托的推斷,並沒有new 委托這個步驟,而是直接將Function 指定給委托。
(2).匿名函數
1 public delegate string TeaDelegate(string spText); 2 3 public class DelegateSource 4 { 5 public void TestDelegate() 6 { 7 Operator op = new Operator(); 8 bool flag = true; 9 TeaDelegate tea = delegate(string spText) 10 { 11 if (spText == "去給我倒杯水") 12 { 13 if (flag) 14 { 15 return "老公,茶來了"; 16 } 17 else 18 { 19 return "老公,沒有水了"; 20 } 21 } 22 return "等待......."; 23 }; 24 25 Console.WriteLine("去給我倒杯水"); 26 Console.WriteLine(); 27 string result=tea("去給我倒杯水"); 28 Thread.Sleep(5000); 29 Console.WriteLine(result); 30 Console.WriteLine(); 31 } 32 }View Code
至於匿名委托,給人的感覺更為直接了,都不用顯示的指定方法名,因為根本沒有方法,而是指定的匿名方法。匿名方法在.NET 中提高了
代碼的可讀性和優雅性。對於更多操作較少的方法直接寫為匿名函數,這樣會大大提高代碼的可讀性。這裡有兩個值得註意的地方: 第一,不能使用
跳轉語句跳轉到該匿名方法外,第二 不能使用ref,out修飾的參數
(3).多播委托
1 public delegate string TeaDelegate(string spText); 2 3 public class DelegateSource 4 { 5 public void TestDelegate() 6 { 7 Operator op = new Operator(); 8 9 TeaDelegate tea1 = op.GetTea; 10 TeaDelegate tea2 = op.Speak; 11 TeaDelegate tea = tea1 + tea2; 12 13 Console.WriteLine("去給我倒杯水"); 14 Console.WriteLine(); 15 string result=tea("去給我倒杯水"); 16 Thread.Sleep(5000); 17 Console.WriteLine(result); 18 Console.WriteLine(); 19 } 20 } 21 22 public class Operator 23 { 24 /// <summary> 25 /// 確定是否還有水 26 /// </summary> 27 private bool flag = true; 28 29 public string GetTea(string spText) 30 { 31 if (spText == "去給我倒杯水") 32 { 33 if (flag) 34 { 35 return "老公,茶來了"; 36 } 37 else 38 { 39 return "老公,沒有水了"; 40 } 41 } 42 return "等待......."; 43 } 44 45 46 public string Speak(string spText) 47 { 48 Console.WriteLine("\n去把我的設計圖稿拿來"); 49 return null; 50 } 51 }View Code
還是上面的那個實例,我不盡想讓女朋友去給我掉杯水,還讓她幫我將程式設計圖稿拿過來。這個時候做的就不是一件事了,而是多件。
程式中也有很多這種情況,於是我們需要多播委托,在一個委托上指定多個執行方法,這是在程式中可以行的。上面提到了,委托直接繼承於
System.MulticastDelegate,正是因為這個類可以實現多播委托。如果調用多播委托,就可以按順序連續調用多個方法。為此,委托的簽名就必須返回void;否則,就只能得到委托調用的最後一個方法的結果。所以在上面的這段代碼中是得不到結果的
4.事件
使用C#編程,無論是WinForm,WebForm 給人很難忘得就是它的控制項,而他們的控制項庫使用方式都是使用使用事件驅動模式,而事件驅動模式卻少不了委托。話不多說,看代碼能夠更清好的理解事件和委托之間的聯繫.
1 public delegate void MyDelegate(string name); 2 3 public class EventSource 4 { 5 public event MyDelegate Event_Delegate; 6 7 public void SetCustomer(string name) 8 { 9 Console.WriteLine("事件發生.....\n"); 10 Console.WriteLine("hi! "+name); 11 } 12 13 public void TestEvent() 14 { 15 EventSource source = new EventSource(); 16 Console.WriteLine("訂閱事件.....\n"); 17 source.Event_Delegate += new MyDelegate(source.SetCustomer); 18 Console.WriteLine("觸發事件.....\n"); 19 source.Event_Delegate("hechen"); 20 Console.WriteLine(".................."); 21 } 22 }View Code
上面的代碼中我們定義了一個委托,然後定義了一個類EventSource,這個類中聲明瞭一個事件。定義一個事件使用event 關鍵字,定義一
個event必須指定這個event傳遞消息的委托,在觸發事件之前必需訂閱事件,我們使用+= new 語法來訂閱一個事件,也就相當於實例化一個事件。
當我們觸發事件的時候,就會調用相應的方法去處理。
5. 泛型委托
委托是類型安全的引用,泛型委托就和我們常用的泛型類一樣,這個類在使用的時候才能確定類型.通過泛型委托,我們可以在委托傳遞參數
之後知道它的類型.在.NET中有一個很典型的泛型委托:
public delegate voie EventHandler<TEventArgs>(object sender,TEventArgs e) where TEventArgs:EventArgs.
這是一個非常有特色的泛型委托,可能我們用的比較少,但是作用是不能忽視的。 我們看看三個非常具有代表性的泛型委托.現在.NET4.0已經出來了,但是泛型委托.NET2.0就出來了,Linq 大家用的那叫一個甜,
為啥 函數式編程風格,匿名方法,Lamda表達式表達式使用是如此的魅力。但是大家仔細觀察過沒有,Linq 中的方法有幾個經常出現的參數:
Action<T>,Predicate<T>,Func<T, Result>
Func<T, E>:封裝一個具有一個參數並返回 E 參數指定的類型值的方法,T 是這個委托封裝方法的參數類型,E是方法的返回值類型。當然Func<T, Result> 只是其中的一種情況,這個委托還有其他的幾種情況:Func<T> 這個是方法沒有參數,返回值類型是T;Func<T1,T2,Result> 這個方法有兩個參數,類型分別為T1,T2,返回值是Result,還有Func<T1,T2,T3,Result>,Func<T1,T2,T3,T4,Result> 這幾中情況,具體情況就不介紹了.我們還可以通過擴展類型,擴展為更多的參數.
1 public void TestFunc() 2 { 3 TEventSource eventSource=new TEventSource(); 4 Func<string, string> func = eventSource.GetTea; 5 string result = func("茶"); 6 Console.WriteLine(result); 7 } 8 9 public string GetTea(string context) 10 { 11 if (context == "茶") 12 { 13 return "茶來了"; 14 } 15 else 16 { 17 return "設計稿子來了"; 18 } 19 }View Code
Action<T>:封裝一個方法,該方法只採用一個參數並且不返回值,包括Action<T>,Action<T1,T2>,Action<T1,T2,T3>,Action<T1,T2,T3,T4> 這幾種情況,也可以通過擴展方法去擴展參數的個數 。
1 public void TestAction() 2 { 3 TEventSource eventSource = new TEventSource(); 4 Action<string> action = eventSource.Speak; 5 action("Action<T> 泛型委托"); 6 } 7 8 public void Speak(string context) 9 { 10 Console.WriteLine(context); 11 }View Code
Predicate<T>:表示定義一組條件並確定指定對象是否符合這些條件的方法。該委托返回的是一個bool類型的值,如果比較滿足條件
返回true,否則返回false.其實上面的Func 委托可以包含這個委托.不過這個委托和上面的兩個不一樣,它只有一種類型
1 public void TestPredicate() 2 { 3 TEventSource eventSource = new TEventSource(); 4 Predicate<int> predicate = eventSource.IsRigth; 5 Console.WriteLine(predicate(0)); 6 } 7 8 public bool IsRigth(int value) 9 { 10 if (value == 0) 11 { 12 return true; 13 } 14 else 15 { 16 return false; 17 } 18 }View Code
6.非同步委托
投票技術: 委托其實相當於一個線程,使用投票技術是使用非同步委托的一種實現方式.Delegate類提供了方法BeginInvoke(),可以傳送委托類型定義的輸入參數,其返回類型為IAsyncResult。IAsyncResult的IsCompleted屬性可以判斷委托任務是否完成
1 public delegate int DelegateVote(int data, int ms); 2 /// <summary> 3 /// 使用投票操作完成委托任務 4 /// </summary> 5 public class VoteDelegate 6 { 7 /// <summary> 8 /// 休眠特定時間執行操作 9 /// </summary> 10 /// <param name="data"></param> 11 /// <param name="ms"></param> 12 /// <returns></returns> 13 public static int TakeWork(int data, int ms) 14 { 15 Console.WriteLine("開始調用TakeWork方法"); 16 Thread.Sleep(ms); 17 Console.WriteLine("結束調用TakeWork方法"); 18 return data + 10; 19 } 20 21 public void TestDelegate() 22 { 23 DelegateVote voteDel = TakeWork; 24 IAsyncResult result = voteDel.BeginInvoke(1,5000,null,null); 25 while (result.IsCompleted == false) 26 { 27 Console.WriteLine("等待......"); 28 Thread.Sleep(500); 29 } 30 int value = voteDel.EndInvoke(result); 31 Console.WriteLine("委托調用結果: "+value); 32 } 33 }View Code
等待句柄:等待句柄是使用AsyncWaitHandle屬性訪問,返回一個WaitHandle類型的對象,它可以等待委托線程完成其任務。在這個參數中可以設置最大的等待時間。
1 public delegate string WaitDelegate(string content); 2 3 public class WaitHandlerDelegate 4 { 5 public void TestWaitHander() 6 { 7 WaitDelegate del = GetTea; 8 IAsyncResult ar = del.BeginInvoke("hechen", null, null); 9 while (true) 10 { 11 Console.Write("."); 12 if (ar.AsyncWaitHandle.WaitOne(50, false)) 13 { 14 break; 15 } 16 } 17 string result=del.EndInvoke(ar); 18 Console.WriteLine(result); 19 20 } 21 22 public static string GetTea(string content) 23 { 24 return "茶來了 "+content; 25 } 26 }View Code
非同步回調:這個方式和投票技術有點類似,不過在投票方式中BeginInvoke()方法第三個參數指定了一個方法簽名,而這個方法參數接收IAsyncResult 類型的參數。
1 public delegate string AsyDelegate(string content); 2 3 public class AsyncresultDelegate 4 { 5 public void TestAsync() 6 { 7 AsyDelegate del = GetTea; 8 del.BeginInvoke("hechen", delegate(IAsyncResult ar) { 9 Thread.Sleep(5000); 10 string result = del.EndInvoke(ar); 11 Console.WriteLine(result); 12 }, null); 13 for (int i = 0; i < 100; i++) 14 { 15 Console.WriteLine("等待....."); 16 Thread.Sleep(1000); 17 } 18 } 19 20 public static string GetTea(string content) 21 { 22 return "茶來了 " + content; 23 } 24 }View Code