文章出處

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     }
復制代碼

 

 

輸出結果

 

上面使用最普通的一種方式來定義了一個委托的使用,這個例子雖然很簡單,但是能夠很形象的描述委托的使用。

 

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     }
復制代碼

 

在委托定義的例子中我們看到委托的使用方法是在委托實例化的時候指定的[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     }
復制代碼

 

至于匿名委托,給人的感覺更為直接了,都不用顯示的指定方法名,因為根本沒有方法,而是指定的匿名方法。匿名方法在.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     }
復制代碼

 

  還是上面的那個實例,我不盡想讓女朋友去給我掉杯水,還讓她幫我將程序設計圖稿拿過來。這個時候做的就不是一件事了,而是多件。

程序中也有很多這種情況,于是我們需要多播委托,在一個委托上指定多個執行方法,這是在程序中可以行的。上面提到了,委托直接繼承于

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     }
復制代碼

 

上面的代碼中我們定義了一個委托,然后定義了一個類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<stringstring> 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         }
復制代碼

 

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         }
復制代碼

 

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         }
復制代碼

 

 

6.異步委托

投票技術: 委托其實相當于一個線程,使用投票技術是使用異步委托的一種實現方式.Delegate類提供了方法BeginInvoke(),可以傳送委托類型定義的輸入參數,其返回類型為IAsyncResult。IAsyncResult的IsCompleted屬性可以判斷委托任務是否完成

異步委托投票技術

 

 

等待句柄:等待句柄是使用AsyncWaitHandle屬性訪問,返回一個WaitHandle類型的對象,它可以等待委托線程完成其任務。在這個參數中可以設置最大的等待時間。

異步委托等待句柄

 

 

異步回調:這個方式和投票技術有點類似,不過在投票方式中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     }

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()