日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#委托、事件、消息(入门级)

發布時間:2024/10/12 C# 101 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#委托、事件、消息(入门级) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  本文的內容需要一定的OOP知識,不過我會在另一個內容介紹,由于實現細節,我會先寫這部分。

  大家關于委托、事件和消息大多是從WinForm編程接觸的,首先是你在可視化的設計器里雙擊控件寫所謂事件的處理代碼,讓編譯器幫你做其他事情。然后可能你會聽說,事件是和委托有關系的,你可能又會聽說事件處理機制和消息也是有關系的。

  那么什么是委托,什么是事件,什么是消息呢?

  不急,我們從委托開始。

  委托是什么?我先撂一句話:委托是方法的類型。

  為什么這么說,大家知道,從類和對象的關系來看。對象是類的實例,類是對象的類型,類是對象的抽象,對象擁有類的所有屬性和方法,委托和方法的關系也是如此。(如果覺得理解方便的話,你也可以先把委托和方法的關系理解為繼承中父類抽象方法的和子類實現其抽象方法關系。)

  接下來,我們將通過幾個小例子來初步認識委托。

  ①打招呼的例子(改編自來自網絡的例子)

  我們需要實現一個方法:打招呼(SayHi)。

  我們可能會在利用如下代碼向控制臺輸出:?

1 using System;
2
3 namespace DelagateEG
4 {
5 public class Program
6 {
7 public static void Main(string[] args)
8 {
9 Console.WriteLine("早上好!");
10 //Console.ReadKey();
11 }
12 }
13 }
?

  這是十分基礎的,那么我們加大難度:我們希望通過傳入參數來實現對某個具體的人物對象打招呼。

  可能您會利用如下代碼實現:

1 using System;
2
3 namespace DelagateEG
4 {
5 public class Program
6 {
7 public static void Main(string[] args)
8 {
9 SayHi("Johness");
10 //Console.ReadKey();
11        // 輸出 早上好!Johness
12 }
13
14 public static void SayHi(string name)
15 {
16 Console.WriteLine("早上好! " + name);
17 }
18 }
19 }

  不知道大家覺得怎么樣?反正我覺得這樣不好,只說一點吧:我傳過去的是什么?是一個英文名,那么為什么還是用中文的打招呼?讓人家外國友人聽得懂可以不?

  可能有的朋友會想在SayHi方法主體里判斷。如下:

1 public static void SayHi(string name)
2 {
3 if (/*判斷是輸入的中文姓名*/)
4 {
5 Console.WriteLine("早上好! " + name);
6 }
7 else
8 {
9 Console.WriteLine("Morning! " + name);
10 }
11 }

  但是您不會這樣做吧?這樣做簡直就失敗了,首先:如果我是中國人,我英語才入門,或者說根本不懂英語,隨便給自己來了個英文名,你跟我說“Morning”我根本聽不懂;其次,如果現在除了中文和英文,我還需要程序用其他語言打招呼,不可能加繼續加if-else吧。

  那么,我們可以再傳遞一個代表語言的參數,比如枚舉。當然,現在我們用字符串做一個例子出來。

1 public static void SayHi(string name,string lanType)
2 {
3 switch(lanType)
4 {
5 case "Chinese":
6 Console.WriteLine("早上好! " + name);
7 break;
8 case "English":
9 Console.WriteLine("Morning! " + name);
10 break;
11 // case
12          // ……
13
14 default:
15 break;
16 }
17 }

  這樣是否可以了呢?不得不說,這樣確實解決了問題,但是如果我需要添加新語言,那么是不是我需要修改SayHi方法呢?而且如果是使用枚舉作為另一個參數,就還得添加新的枚舉。程序的可擴展性就會很差。

  在實現真正地將判斷的方法提取出來之前我們將代碼略做修改,使它更接近面向對象的一些規則:

1      // 判斷語言并打招呼
2 public static void SayHi(string name,string lanType)
3 {
4 switch(lanType)
5 {
6 case "Chinese":
7 ChineseSayHi(name);
8 break;
9 case "English":
10 EnglishSayHi(name);
11 break;
12 // case
13          // ……
14
15 default:
16 break;
17 }
18 }
19
20 // 中文的打招呼
21 public static void ChineseSayHi(string name)
22 {
23 Console.WriteLine("早上好! " + name);
24 }
25
26 // 英文的打招呼
27 public static void EnglishSayHi(string name)
28 {
29 Console.WriteLine("Morning! " + name);
30 }
31
32 // ……其他語言

  大膽想象一下:如果我們要真正實現將判斷語言的方法和打招呼的方法分離該怎么處理?

  也許我們可以利用下面的代碼(可能有一些牽強):

1      /// <summary>
2      /// 判斷語言類型并打招呼的方法
3      /// </summary>
4      /// <param name="name">任務姓名</param>
5      /// <param name="lanType">語言類型</param>
6 public static void JudgeLanguageAndSayHi(string name,LanguageType lanType)
7 {
8 // 判斷語言
9 switch (lanType)
10 {
11 case LanguageType.Chinese:
12 SayHi(name, ???);
13 break;
14 case LanguageType.English:
15 SayHi(name, ???);
16 break;
17 // case
18             // ……
19
20 default:
21 break;
22 }
23 }

  以上是一個判斷語言的方法,在它的方法內部通過分支結構判斷語言類型,然后調用了打招呼的方法。

  這樣可能有一些牽強,但是我們先看一下,上面的代碼中???代表什么?我大膽地猜想是不是可以將具體的打招呼的方法傳遞給SayHi方法,???代表的就是諸如ChineseSayHi或者EnglishSayHi的方法:

1      /// <summary>
2      /// 打招呼的方法
3      /// </summary>
4      /// <param name="name">人物姓名</param>
5      /// <param name="concreteMethod">具體的打招呼的方法</param>
6 public static void SayHi(string name, *** concreteMethod)
7 {
8 concreteMethod(name);
9 }
10
11
12 /// <summary>
13   /// 語言類型的枚舉
14   /// </summary>
15 public enum LanguageType
16 {
17 /// <summary>
18      /// 中文
19 /// </summary>
20 Chinese,
21 /// <summary>
22      /// 英文
23      /// </summary>
24 English
25
26 // ……其它語言
27 }

  當判斷了語言類型后,我們調用SayHi方法,并傳遞對應的方法作為參數,而SayHi方法在內部調用作為參數傳遞的具體方法來打招呼 ( 大家可能覺得這樣繞了一些彎,不過有時候確實需要這樣做,比如異常處理) 。

  把方法作為參數傳遞,可能嗎?通過類與對象的關系,我們可以得知,我們想將對象作為參數傳遞給方法需要找到能代表該對象的類型,比如傳遞字符串作為參數傳遞給打招呼的方法,我們就必須在方法聲明時指定要接收一個string類型的參數:

1      // 中文的打招呼
2 public static void ChineseSayHi(string name)
3 {
4 Console.WriteLine("早上好! " + name);
5 }
6
7 // 英文的打招呼
8 public static void EnglishSayHi(string name)
9 {
10 Console.WriteLine("Morning! " + name);
11 }
12
13 // ……其他語言

  這個很好理解是吧?但是如果把方法作為參數傳遞的話我們該用什么類型來代表它呢?

  C#里有這樣的類型:委托。

  委托和類是同級的概念,如同類規定了它的對象(或實例)應該有的成員和方法一樣,委托也規定了它的實例(即方法)的屬性:參數和返回值。

  我們可以使用delegate聲明委托:

1 public delegate void concreteMethod(string name);

  委托規定了它的實例的屬性,比如以上的委托,它規定了實例(即具體的方法):必須只有一個字符串類型的參數,必須返回void。它并沒有規定子類的具體實現方式。

  也就是說上面的委托代表了一類“以一個字符串類型作為參數,并且返回void”的方法。

1      [public|private|protected|……] [……] [static] [……] void MethodName(string param)
2 {
3 statement1;
4 statement2;
5 statement3;
6 statement4;
7 }

  我先將使用了委托的解決方案貼出來:

1 using System;
2
3 namespace DelagateEG
4 {
5 /// <summary>
6   /// 定義委托
7   /// </summary>
8   /// <param name="name"></param>
9 public delegate void ConcreteMethod(string name);
10
11 public class Program
12 {
13 public static void Main(string[] args)
14 {
15 JudgeLanguageAndSayHi("Johness", LanguageType.Chinese);
16 JudgeLanguageAndSayHi("阿何", LanguageType.English);
17 // 輸出 早上好! Johnesss
18        // Morning! 阿何
19 }
20
21 /// <summary>
22      /// 判斷語言類型并打招呼的方法
23      /// </summary>
24      /// <param name="name">任務姓名</param>
25      /// <param name="lanType">語言類型</param>
26 public static void JudgeLanguageAndSayHi(string name, LanguageType lanType)
27 {
28 // 判斷語言
29 switch (lanType)
30 {
31 case LanguageType.Chinese:
32 SayHi(name, ChineseSayHi);
33 break;
34 case LanguageType.English:
35 SayHi(name, EnglishSayHi);
36 break;
37 // case
38           // ……
39
40 default:
41 break;
42 }
43 }
44
45 /// <summary>
46      /// 打招呼的方法
47      /// </summary>
48      /// <param name="name">人物姓名</param>
49      /// <param name="concreteMethod">具體的打招呼的方法</param>
50 public static void SayHi(string name, ConcreteMethod concreteMethod)
51 {
52 concreteMethod(name);
53 }
54
55 // 中文的打招呼
56 public static void ChineseSayHi(string name)
57 {
58 Console.WriteLine("早上好! " + name);
59 }
60
61 // 英文的打招呼
62 public static void EnglishSayHi(string name)
63 {
64 Console.WriteLine("Morning! " + name);
65 }
66
67 // ……其他語言
68
69 }
70
71 /// <summary>
72    /// 語言類型的枚舉
73    /// </summary>
74 public enum LanguageType
75 {
76 /// <summary>
77      /// 中文
78      /// </summary>
79 Chinese,
80 /// <summary>
81      /// 英文
82      /// </summary>
83 English
84
85 // ……其它語言
86 }
87 }

  個人建議:和類同級的如委托和枚舉最好放在命名空間下而不放在類內部(并非絕對)。

  我們再來梳理一下整個過程:

  一,定義一個具有一個字符串參數的沒有返回值的委托,我們會使用它來代表我們具體的打招呼的方法。

  二,定義一個判斷語言的方法,在方法內部我們調用打招呼的方法,并將對應語言的具體打招呼的方法傳遞給它,讓它在內部調用。

  三,定義相關枚舉和具體方法并測試。

  如果不能理解的話也不用著急,只需要記住一點:把它當作類來看,只要記住一點:它的實例是方法就可以了。

?

  剛剛的例子大家肯定會覺得多此一舉,如果不會的話,可能您對OOP的理解有一些深入了,也可能您并沒有進入我所營造的環境。

  如果我估計得沒錯的話,肯定會有朋友會糾結我為什么在JudgePanguageAndSayHi判斷后不直接調用具體的方法。雖然我解釋了很多遍,但是如果實在是糾結的話我可以告訴大家:可以跳過中間環節,也是用委托來做。當然,這可能會困難一些。

  我先將修改過的代碼貼出來再為大家解釋:

1 using System;
2
3 namespace DelagateEG
4 {
5 /// <summary>
6    /// 定義委托
7    /// </summary>
8    /// <param name="name"></param>
9 public delegate void ConcreteMethod(string name);
10
11 public class Program
12 {
13 /// <summary>
14      /// 定義委托類型的屬性
15      /// </summary>
16 private static ConcreteMethod SayHi;
17
18 public static void Main(string[] args)
19 {
20 JudgeLanguage(LanguageType.Chinese);
21 SayHi("Johness");
22 JudgeLanguage(LanguageType.English);
23 SayHi("阿何");
24 // 輸出 早上好! Johnesss
25        // Morning! 阿何
26 }
27
28 /// <summary>
29      /// 純粹的判斷語言的方法
30      /// 可以改一下名字
31      /// 最好改為“修改或設置語言”
32      /// </summary>
33      /// <param name="languageType"></param>
34 private static void JudgeLanguage(LanguageType languageType)
35 {
36 // 判斷語言
37 switch (languageType)
38 {
39 case LanguageType.Chinese:
40 // 直接指定委托代表的具體方法
41 SayHi = ChineseSayHi;
42 break;
43 case LanguageType.English:
44 // 委托的構造方法指定其代表的方法
45 SayHi = new ConcreteMethod(EnglishSayHi);
46 break;
47 // case
48          // ……
49
50 default:
51 break;
52 }
53 }
54
55 // 中文的打招呼
56 public static void ChineseSayHi(string name)
57 {
58 Console.WriteLine("早上好! " + name);
59 }
60
61 // 英文的打招呼
62 public static void EnglishSayHi(string name)
63 {
64 Console.WriteLine("Morning! " + name);
65 }
66
67 // ……其他語言
68
69 }
70
71 /// <summary>
72   /// 語言類型的枚舉
73   /// </summary>
74 public enum LanguageType
75 {
76 /// <summary>
77      /// 中文
78      /// </summary>
79 Chinese,
80 /// <summary>
81      /// 英文
82      /// </summary>
83 English
84
85 // ……其它語言
86 }
87 }

  這一次我修改了兩處地方:一是在類內部增加了一個委托類型的屬性,這個應該不難理解吧?相當于你自己定義的學生類中有一個字符串類型的屬性你將它用來代表學生的姓名一樣;二是我直接不要了原來的SayHi方法(請大家看看我的注釋,由于時間關系我先寫到這兒了)。

?

  我完整地寫了一個例子,讓學生給大家打招呼:

1 using System;
2
3 namespace DelagateEG
4 {
5 /// <summary>
6   /// 語言類型的枚舉
7   /// </summary>
8 public enum LanguageType
9 {
10 /// <summary>
11      /// 中文
12      /// </summary>
13 Chinese,
14 /// <summary>
15      /// 英文
16      /// </summary>
17 English
18
19 // ……其它語言
20 }
21 } 1 using System;
2
3 namespace DelagateEG
4 {
5
6 /// <summary>
7    /// 委托
8    /// 它本身和打招呼沒有任何關系
9    /// 只是它能代表的一類方法中包括了我們需要用到的打招呼的方法
10    /// </summary>
11    /// <param name="name"></param>
12 public delegate void SayHiHandler();
13
14 /// <summary>
15   /// 學生類
16   /// </summary>
17 public class Student
18 {
19 // 學生姓名和年齡
20 public string Name { get; set; }
21 public int Age { get; set; }
22
23 // 學生使用的語言
24 public LanguageType Language { get; set; }
25
26 // 委托成員,我們用它來保存具體的打招呼的方法
27 private SayHiHandler _sayHi;
28 // 封裝委托,讓外界通過SayHi訪問_syHi
29      // 由于委托是方法的類型,所以我們可以將它當方法用
30      // 在外界訪問它之前將它綁定到具體方法
31 public SayHiHandler SayHi
32 {
33 get
34 {
35 switch (Language)
36 {
37 case LanguageType.Chinese:
38 _sayHi = ChineseSayHi;
39 break;
40 case LanguageType.English:
41 _sayHi = new SayHiHandler(EnglishSayHi);
42 break;
43 // ……
44
45 default:
46 break;
47 }
48 return _sayHi;
49 }
50 }
51
52 // 在構造函數中初始化一些值
53 public Student()
54 {
55 Name = "Johness";
56 Language = LanguageType.Chinese;
57 }
58
59 // 中文的打招呼
60 public void ChineseSayHi()
61 {
62 Console.WriteLine("大家早上好! 我是" + Name);
63 }
64
65 // 英文的打招呼
66 public void EnglishSayHi()
67 {
68 Console.WriteLine("Morning! I'm" + Name);
69 }
70
71 // ……其他語言
72
73 }
74 } 1 using System;
2
3 namespace DelagateEG
4 {
5 public class Program
6 {
7 public static void Main(string[] args)
8 {
9 // 使用默認值訪問打招呼的方法
10 new Student().SayHi();
11
12 // 使用自定義的內容打招呼
13 new Student { Name = "阿何", Language = LanguageType.English }.SayHi();
14 Console.ReadKey();
15
16 /*
17 *
18 * 輸出 大家早上好! 我是Johness
19 * Morning! I'm 阿何
20 *
21         */
22 }
23 }
24 }

  先介紹到這兒。

2012-04-01?18:26:05  

  現在介紹第二個例子,是我們老師在講解委托時使用的。由于我是自學的,很多知識并不系統和全面,借用老師的例子可能大家更容易理解。

  ②顯示拷貝進度。

  我們都使用過文件或者文件夾的拷貝或者移動。我們在拷貝或移動文件夾時,系統會顯示一個進度條表示當前進行了多少個文件的拷貝。那么現在,要求我們通過程序代碼實現這個功能,我們會怎么做呢?

  假如我們拷貝文件夾(即目錄) 的方法是這樣的:

1      /// <summary>
2      /// 拷貝目錄的方法
3      /// </summary>
4 public static void CopyDir()
5 {
6 // 假如我們拷貝的目錄有100個文件
7        // 進行一次循環即完成一個文件的拷貝
8 for (int i = 0; i < 100; i++)
9 {
10
11 }
12 }

  那么,我們可能比較簡單的寫法是在for循環的內部寫實現。比如:

1      /// <summary>
2     /// 拷貝目錄的方法
3      /// </summary>
4 public static void CopyDir()
5 {
6 // ……
7        // ……
8 for (int i = 0; i < 100; i++)
9 {
10 Console.WriteLine("當前拷貝{0}/{1}", i + 1, 100);
11 }
12 }

  當然,這樣寫是可以完成在控制臺輸出。但是如果我們在WinForm程序的窗體中顯示進度條呢?當然,有的朋友可能會說:我們可以傳遞一個進度條控件到方法內部。那么我可能頻繁地切換進度條顯示(比如說在控制臺下和界面切換)或者使用其它控件或平臺呢?這樣的話程序代碼的復用性就會變得十分低。

  那么我們怎么解決這個問題呢?使用委托可以嗎?

  答案是肯定的。

  我們利用如下代碼解決這個問題:

1 using System;
2 using System.Threading;
3
4 namespace DelagateEG
5 {
6 // 委托,用于代表顯示拷貝進度的方法
7 public delegate void ShowMsgHanler(int current, int max);
8
9 public class Program
10 {
11 public static void Main(string[] args)
12 {
13 // 我們測試一下代碼
14
15        // 測試用第一種方法顯示進度
16 ShowMsgHanler showMsag1 = new ShowMsgHanler(ShowMsg1);
17 CopyDir(showMsag1);
18 Console.ReadKey();
19 /* 輸出
20 *
21 * 當前拷貝1/100
22 當前拷貝2/100
23 * ……
24 當前拷貝99/100
25 當前拷貝100/100
26         */
27
28 // 測試第二種方法
29 ShowMsgHanler showMsag2 = new ShowMsgHanler(ShowMsg2);
30 CopyDir(showMsag2);
31 Console.ReadKey();
32 /* 輸出
33 *
34 * 當前拷貝第1個文件,總共100
35 當前拷貝第2個文件,總共100
36 ……
37 當前拷貝第99個文件,總共100
38 當前拷貝第100個文件,總共100
39         */
40 }
41
42 /// <summary>
43      /// 拷貝目錄的方法
44      /// </summary>
45      /// <param name="showMsg">
46      /// 顯示進度的方法
47      /// </param>
48 public static void CopyDir(ShowMsgHanler showMsg)
49 {
50 // 加入我們拷貝的目錄有100個文件
51        // 進行一次循環即完成一個文件的拷貝
52 for (int i = 0; i < 100; i++)
53 {
54 showMsg(i, 100);
55 // 為了顯示更清晰
56          // 可以使用如下代碼讓拷貝進度慢一些
57          // Thread.Sleep(300);
58 }
59 }
60
61 /// <summary>
62      /// 實現顯示進度的方法1
63      /// </summary>
64      /// <param name="current">當前拷貝文件</param>
65      /// <param name="max">總文件數</param>
66 public static void ShowMsg1(int current, int max)
67 {
68 Console.WriteLine("當前拷貝{0}/{1}", current + 1, max);
69 }
70
71 /// <summary>
72      /// 實現顯示進度的方法2
73      /// 用于對比方法1
74      /// </summary>
75 public static void ShowMsg2(int current, int max)
76 {
77 Console.WriteLine("當前拷貝第{0}個文件,總共{1}", current + 1, max);
78 }
79 }
80 }

  那么如果這樣寫了,我可以用方法三做界面的進度條顯示,方法四用在網頁上,等等……這種做法在實際編程上用得十分多。

  而且也符合“開閉原則”。

2012-04-02 19:58:11


?





?

轉載于:https://www.cnblogs.com/Johness/archive/2012/04/01/2428959.html

總結

以上是生活随笔為你收集整理的C#委托、事件、消息(入门级)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。