【C#系列】你應該知道的委托和事件

来源:https://www.cnblogs.com/wangjiming/archive/2018/01/19/8300103.html
-Advertisement-
Play Games

本篇文章更適合具有一定開發經驗,一定功底,且對底層代碼有所研究的朋友!!! 本篇文章主要採用理論和代碼實例相結合方式來論述委托和事件,涉及到一些邊界技術,如軟體架構的OCP原則(開-閉原則), 軟體架構解耦,設計模式(Sender-Order)和事件驅動模型,有一定難度和深度,不適合初級者。 第一部 ...


本篇文章更適合具有一定開發經驗,一定功底,且對底層代碼有所研究的朋友!!!

 

本篇文章主要採用理論和代碼實例相結合方式來論述委托和事件,涉及到一些邊界技術,如軟體架構的OCP原則(開-閉原則),

軟體架構解耦,設計模式(Sender-Order)和事件驅動模型,有一定難度和深度,不適合初級者。

第一部份   委托

關於委托內容,主要圍繞下圖來論述。

 一   委托是什麼(what)

(一)委托產生的背景之一

1.我們先來假設這樣一個情景需求:

   設計一個系統,使其滿足如下條件:

   (1)當前,只有中國人和英國人使用該系統;

   (2)向系統輸入用戶名和相應的語言,將產生相應語言的問候語;

      

  (3)後期,可能會有其他國家語言加入該系統(系統變化的部分) ;

 2.技術方案實現

關於技術方案實現,我們可以採用下圖中的三種方式之一。

為了更好地敘述委托,我們分別實現三種技術方案,並找出它們的關係。

 2.1 一般實現

Code(控制台程式)

 1 using System;
 2 
 3 namespace DelegateDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Console.WriteLine(GetGreetingContens("小王", "Chinese"));
10             Console.WriteLine(GetGreetingContens("Alan_beijing", "English"));
11             Console.WriteLine(GetGreetingContens("Linda", "Russian"));
12             Console.Read();
13         }
14 
15         //根據用戶名和語言,獲取問候語
16         public static string GetGreetingContens(string UserName, string Language)
17         {
18             //New 一個GreetToUsers對象
19             GreetToUsers greetToUsers = new GreetToUsers();
20             //當然,你也可以使用switch開發語句來代替如下的if......else......
21             if (Language == "Chinese")
22             {
23                 return greetToUsers.ChinesePeople(UserName);
24             }
25             else if (Language == "English")
26             {
27                 return greetToUsers.EnglishPeople(UserName);
28             }
29             else
30             {
31                 return "抱歉,當前系統只支持漢語與英語(Sorry, the current system only supports Chinese and English.)";
32             }
33         }
34     }
35 
36 
37 
38     //定義基本問候類和方法
39     public class GreetToUsers
40     {
41         //Chinese People
42         public string ChinesePeople(string UserName)
43         {
44             string GreetContents = "您好!" + UserName;
45             return GreetContents;
46         }
47 
48         //English People
49         public string EnglishPeople(string UserName)
50         {
51             string GreetContents = "Hello," + UserName + "!";
52             return GreetContents;
53         }
54     }
55 
56 }
View Code

 Result

分析

 

2.2用介面實現

如上,我們分析了方案一中的問題,為了更好地解決方案一存在的問題,我們採用面向介面編程的形式來實現。

2.2.1  什麼是面向介面編程?

面向介面編程,主要是解決軟體架構設計中“動靜問題”,即封裝不變(靜),剝離變化(抽出變化)。

 Code:

 1 using System;
 2 
 3 namespace DelegateDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             GreetToChineseUsers greetToChinesUsers = new GreetToChineseUsers();
10             GreetToEnglishUsers greetToEnglishUsers = new GreetToEnglishUsers();
11             GreetToOtherUsers greetToOtherUsers = new GreetToOtherUsers();
12             //Chinse Users
13             IGreetToUsers iGreetToChineseUsers = greetToChinesUsers;
14             Console.WriteLine(iGreetToChineseUsers.CountryPeople("小王", "Chinese"));
15             //English Users
16             IGreetToUsers iGreetToEnglishUsers = greetToEnglishUsers;
17             Console.WriteLine(iGreetToEnglishUsers.CountryPeople("Alan_beijing", "English"));
18             //Other Users
19             IGreetToUsers iGreetToOtherUsers = greetToOtherUsers;
20             Console.WriteLine(iGreetToOtherUsers.CountryPeople("Linda", "Russian"));
21              
22             Console.Read();
23         }
24 
25         
26     }
27 
28     //系統輸出問候語(變化的部分,語言為變化因數)
29     public interface IGreetToUsers 
30     {
31         string CountryPeople(string UserName,string Language);
32     }
33 
34 
35     //漢語用戶類
36     public class GreetToChineseUsers:IGreetToUsers
37     {
38         //Chinese People
39         public string  CountryPeople(string UserName, string Language)
40         {
41             string GreetContents = "您好!" + UserName;
42             return GreetContents;
43         }
44     }
45     
46     //英語用戶類
47     public class GreetToEnglishUsers : IGreetToUsers
48     {
49         //English People
50         public string CountryPeople(string UserName, string Language)
51         {
52             string GreetContents = "Hello," + UserName + "!";
53             return GreetContents;
54         }
55     }
56 
57     //其他用戶類
58     public class GreetToOtherUsers : IGreetToUsers
59     {
60         //English People
61         public string CountryPeople(string UserName, string Language)
62         {
63             return "Sorrry,當前系統只支持漢語與英語";
64         }
65     }
66 
67 }
View Code

result

分析:

(1)如上,我們將變化因數"語言"剝離出來,形成介面,以後只要每增加一個語言,只需實現介面即可,滿足了OCP原則,基本解決了方案一中存在的問題;

(2)如上代碼只是為了演示面向介面編程這個功能,並不完善,感興趣的讀者,可自行完善(如將語言定義為枚舉類型等);

方案二中的代碼,細心的讀者會發現,Main方法中new了三個對象,假若以後系統有300門語言,那豈不New 300個類,這樣的話,也不利於代碼維護呀,怎麼解決這個問題呢?(提示一下,採用設計模式抽象工廠即可解決該問題)

2.3 用委托實現

在這裡,沒接觸過委托的讀者,先跳過這部分,往下讀,看完(三)怎樣使用委托(How to use)後,再來看本部分。

 Code

 1 using System;
 2 
 3 namespace DelegateDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             //根據語言判斷,傳遞哪個方法的參數
10             Console.WriteLine("------------請輸入用戶名------------");
11             string UserName = Console.ReadLine();
12             Console.WriteLine("------------請輸入語言------------");
13             string Language = Console.ReadLine();
14             Console.WriteLine("------------輸出結果------------");
15             GreetToUsers greetToUsers = new GreetToUsers();
16             if (Language == "Chinese")
17             {
18                Console.WriteLine(GetGreetingContents(UserName, greetToUsers.ChinesePeople));
19             }
20             else if (Language == "English")
21             {
22                 Console.WriteLine(GetGreetingContents(UserName, greetToUsers.EnglishPeople));
23             }
24             else
25             {
26                Console.WriteLine(GetGreetingContents(UserName, greetToUsers.OtherPeople));
27             }
28            Console.Read();
29         }
30         
31         
32 
33         public static string GetGreetingContents(string UserName,DelegateGetGreeting delegateGetGreeting)
34         {
35            return delegateGetGreeting(UserName);
36         }
37     }
38 
39     //定義委托
40     public delegate string DelegateGetGreeting(string UserName);
41 
42 
43     //定義基本問候類和方法
44     public  class GreetToUsers
45     {
46         //Chinese People
47         public string ChinesePeople(string UserName)
48         {
49             string GreetContents = "您好!" + UserName;
50             return GreetContents;
51         }
52 
53         //English People
54         public string EnglishPeople(string UserName)
55         {
56             string GreetContents = "Hello," + UserName + "!";
57             return GreetContents;
58         }
59         //非英非漢
60         public string OtherPeople(string UserName)
61         {
62             return "Sorrry,當前系統只支持漢語與英語";
63         }
64     }
65 
66 }
View Code

 Result

 2.3 分析上述三種實現方案的關係

通過上訴三種方式的比較,我們容易得出委托的如下結論:

(1)抽象方法,屏蔽方法細節,調用只需傳遞方法名字即可;

(2)能夠實現程式的解耦,松耦合(在方案一中,GetGreetingContens方法體內new了GreetToUsers對象,強耦合)

(3)委托一般扮演中間者的角色,這功能在委托事件中體現非常明顯(第二部分 事件 將詳細論述)

如我們在租房子時,可以直接找房東(技術實現的一般方法,強耦合,讓租房者和房東直接聯繫),也可找中介(技術實現的委托,松耦合,租房者通過中介來與房東聯繫)

 

2.4 委托背景概述

委托的重要產生背景,就是事件驅動模型(關於什麼是事件和事件驅動,在本文第二部份 事件 論述)。

(二) 委托定義

 用delegate關鍵字定義委托(註意,委托是沒有方法體的,類似介面裡面的方法),在定義委托前,必須明確兩個問題:

1.委托將要綁定的方法;

2.委托的形參類型,形參個數和委托的返回值必須與將要綁定的方法的形參類型,形參個數和返回值一致;

(三)相關概念

委托涉及的相關概念有函數指針,類型安全性、事件、Lambda表達式等

1.函數指針:在C++中,指針的一個類別,主要指向函數(變數指針,主要指向變數地址),可以把C#中的委托理解為函數指針;

2.類型安全性:在C++中,我們都知道指針是類型不安全的(返回值,返回類型和什麼時候返回,這些都是未知的),而委托是類型安全的;

3.事件:可以把事件理解為委托的一種特例(在本文第二部份 事件 論述)

4.Lambda表達式:委托與Lambd表達式相結合,實現高效編程,與Jquery的“較少代碼做更多的事”類似,委托與Lambda,Linq相結合,使較短代碼就能實現比較複雜的功能(在本篇文章中不講解Lambda與Lambda樹,將在後續文章中講解)

(四)委托組成

大致分為兩部分:聲明委托和註冊方法(也叫綁定方法)

1.聲明委托

用delegate聲明;

2.綁定方法

綁定具體方法,傳遞方法名稱;

(五) 委托種類

委托種類,一般分為多播委托和單播委托

1.單播委托:綁定單個方法

2.綁定多個方法

(六) 委托操作

1.綁定方法

2.解綁方法

二  委托能解決什麼問題(Can do)

1.避免核心方法中存在大量的if....else....語句(或swich開關語句);

2.滿足程式設計的OCP原則;

3.使程式具有擴展性;

4.綁定事件;

5.結合Lambda表達式,簡化代碼,高效編程;

6.實現程式的松耦合(解耦),這個在事件(event)中體現比較明顯;

三  怎麼使用委托(How to use)(本篇文章不談匿名委托,匿名委托具體內容,將在Lambda章節講解)

(一)委托的基本構成

通常地,使用委托的步驟與使用類的步驟是一樣的。大致分為兩步:定義委托和綁定方法(傳遞方法)

1.定義委托

用delegate關鍵字定義委托(註意,委托是沒有方法體的,類似介面裡面的方法),在定義委托前,必須明確兩個問題:

(1).委托將要綁定的方法;

(2).委托的形參類型,形參個數和委托的返回值必須與將要綁定的方法的形參類型,形參個數和返回值一致;

public delegate  委托返回類型  委托名(形參)

例子:如上我們委托將要表示系統輸出的問候語

a.委托將要綁定的方法

 public string ChinesePeople(string UserName)
        {
            string GreetContents = "您好!" + UserName;
            return GreetContents;
        }

        //English People
        public string EnglishPeople(string UserName)
        {
            string GreetContents = "Hello," + UserName + "!";
            return GreetContents;
        }
        //非英非漢
        public string OtherPeople(string UserName)
        {
            return "Sorrry,當前系統只支持漢語與英語";
        }

b.由如上方法可看出,方法的返回類型為string,方法有一個string類型的形參,在定義委托時,與其保持一致即可

//定義委托
public delegate string DelegateGetGreeting(string UserName);

2.綁定方法

使用委托時,將方法名字作為參數傳遞給委托即可

1 GreetToUsers greetToUsers = new GreetToUsers();
2 GetGreetingContents(UserName, greetToUsers.ChinesePeople)

(二)委托綁定方法

1.綁定單個方法

綁定單個方法,將單個方法名字傳給委托即可

 1 static void Main(string[] args)
 2         {
 3             //根據語言判斷,傳遞哪個方法的參數
 4             Console.WriteLine("------------請輸入用戶名------------");
 5             string UserName = Console.ReadLine();
 6             Console.WriteLine("------------請輸入語言------------");
 7             string Language = Console.ReadLine();
 8             Console.WriteLine("------------輸出結果------------");
 9             GreetToUsers greetToUsers = new GreetToUsers();
10             DelegateGetGreeting DGG;
11             if (Language == "Chinese")
12             {
13                 //綁定單個方法
14                 DGG = greetToUsers.ChinesePeople;
15                 Console.WriteLine(GetGreetingContents(UserName, DGG));
16             }
17             else if (Language == "English")
18             {
19                 DGG = greetToUsers.EnglishPeople;
20                 Console.WriteLine(GetGreetingContents(UserName, DGG));
21             }
22             else
23             {
24                 DGG = greetToUsers.OtherPeople;
25                 Console.WriteLine(GetGreetingContents(UserName, DGG));
26             }
27             
28             Console.Read();
29         }
View Code

另一種不太規範寫法:不用GetGreetingContents(string UserName,DelegateGetGreeting delegateGetGreeting)方法

 1 static void Main(string[] args)
 2         {
 3             //根據語言判斷,傳遞哪個方法的參數
 4             Console.WriteLine("------------請輸入用戶名------------");
 5             string UserName = Console.ReadLine();
 6             Console.WriteLine("------------請輸入語言------------");
 7             string Language = Console.ReadLine();
 8             Console.WriteLine("------------輸出結果------------");
 9             GreetToUsers greetToUsers = new GreetToUsers();
10             DelegateGetGreeting DGG;
11             if (Language == "Chinese")
12             {
13                 //綁定單個方法
14                 DGG = greetToUsers.ChinesePeople;
15                 Console.WriteLine(DGG(UserName));
16             }
17             else if (Language == "English")
18             {
19                 DGG = greetToUsers.EnglishPeople;
20                 Console.WriteLine(DGG(UserName));
21             }
22             else
23             {
24                 DGG = greetToUsers.OtherPeople;
25                 Console.WriteLine(DGG(UserName));
26             }
27             
28             Console.Read();
29         }
View Code

之所以不規範,主要是在項目中,不利於代碼的模塊化。

2.綁定多個方法(多播委托)

註意:綁定多個方法時,委托範圍類型必須為void類型,否則只返回最後一個綁定的值。

 綁定多個方法,採用 += 綁定

 1 using System;
 2 
 3 namespace DelegateDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9              
10             GreetToUsers greetToUsers = new GreetToUsers();
11             DelegateGetGreeting DGG;
12             
13             //綁定多個方法
14             DGG = greetToUsers.ChinesePeople;
15             DGG += greetToUsers.EnglishPeople;
16             DGG("小王");
17 
18             Console.Read();
19         }
20         
21         
22     }
23 
24     //定義委托
25     public delegate void DelegateGetGreeting(string UserName);
26 
27 
28     //定義基本問候類和方法
29     public  class GreetToUsers
30     {
31         //Chinese People
32         public void ChinesePeople(string UserName)
33         {
34             string GreetContents = "您好!" + UserName;
35             Console.WriteLine(GreetContents);
36         }
37 
38         //English People
39         public void EnglishPeople(string UserName)
40         {
41             string GreetContents = "Hello," + UserName + "!";
42             Console.WriteLine(GreetContents);
43         }
44         //非英非漢
45         public void OtherPeople(string UserName)
46         {
47             Console.WriteLine("Sorrry,當前系統只支持漢語與英語");
48         }
49     }
50 
51 }
52    
53 
54  
View Code

3.解綁方法

解載綁定的方法,採用 -= 解綁

 1 using System;
 2 
 3 namespace DelegateDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9              
10             GreetToUsers greetToUsers = new GreetToUsers();
11             DelegateGetGreeting DGG;
12             
13             //綁定多個方法
14             DGG = greetToUsers.ChinesePeople;
15             DGG += greetToUsers.EnglishPeople;
16 
17             //解綁ChinesePeople方法
18             DGG-= greetToUsers.ChinesePeople;
19             DGG("小王");
20 
21             Console.Read();
22         }
23         
24         
25     }
26 
27     //定義委托
28     public delegate void DelegateGetGreeting(string UserName);
29 
30 
31     //定義基本問候類和方法
32     public  class GreetToUsers
33     {
34         //Chinese People
35         public void ChinesePeople(string UserName)
36         {
37             string GreetContents = "您好!" + UserName;
38             Console.WriteLine(GreetContents);
39         }
40 
41         //English People
42         public void EnglishPeople(string UserName)

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在ASP.NET MVC中,視圖數據可以通過ViewBag、ViewData、TempData來訪問,其中ViewBag 是動態類型(Dynamic),ViewData 是一個字典型的(Dictionary)。 它們的定義如下: 控制器中代碼: 視圖代碼: 運行圖: 當然我們可以在視圖裡面這樣寫: ...
  • 文章參考:https://www.cnblogs.com/wangfuyou/p/6046307.html 1.解決辦法是在web.config增加如下節點到<configuration>下 <system.web.extensions> <scripting> <webServices> <jso ...
  • 方法中調用了其它dll沒有做異常捕獲的處理的方法,即使在本代碼用使用try,cath做異常捕獲,這樣也捕獲不了, 此時需要在本方法頭上使用如下引用即可捕獲到異常: ...
  • 由於精力有限,博主現在只維護這一個系統,會不時的更新代碼提交。 “”小魚企業級開發系統“”的Svn地址:http://code.taobao.org/svn/SF_EDS 項目截圖: 這裡簡單說下系統(字數不夠不讓推首頁。。) 系統的Ioc使用的Spring.Net,MVC框架是微軟自帶的MVC框架 ...
  • "回到目錄" 任務調度組件 1. 位於Scheduling目錄 2. 基類JobBase,所有JOB都派生自它,重寫Cron屬性可以修改調度周期 3. 支持單次JOB,即執行完成後馬上停止 4. 支持對外API介面,以便獲取和修改JOB的列表的狀態 源代碼展現 自定義Job在繼承JobBase之後, ...
  • 繼上文<<基於阻塞隊列的生產者消費者C#併發設計>>的併發隊列版本的併發設計,原文code是基於<<.Net中的並行編程-4.實現高性能非同步隊列>>修改過來的,前面的幾篇文章也詳細介紹了併發實現的其它方案及實現。直接給code: 調用code: 併發系列應該就這樣完了,回頭整理成目錄,自己查起來也方 ...
  • 原文地址:https://www.cnblogs.com/pipi-changing/p/5697481.html 找了下Cron的資料,這篇作者寫的比較清晰,轉載記錄一下,方便後面使用的時候在google 1. cron表達式格式: {秒數} {分鐘} {小時} {日期} {月份} {星期} {年... ...
  • 昨天,在做一個NPOI讀取的小demo的時候,使用OpenFileDialog打開文件,最開始的寫法,直接在按鈕點擊事件中寫,會報錯,代碼如下: 或者直接 這兩種,無論哪種寫法,在代碼執行的時候,會報錯,具體報錯為: 這種情況,在網上查詢,是說線程問題,就是線程衝突了,不知道該執行哪一個,具體說法如 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...