委托和事件

来源:http://www.cnblogs.com/shouce/archive/2016/04/11/5376929.html
-Advertisement-
Play Games

首先來說,委托的作用就是可以給類的方法傳遞其他類的方法而不必將類實例化。第二點,委托就是事件和響應事件的方法的橋梁(就是傳遞響應事件的方法給事件)。這裡要註意,委托跟類平級,事件與方法平級。 全文 事件與委托似乎很難以理解,這是因為它們的使用方式與常用的編碼有很大的差別,例如通常編寫的都是同步代碼, ...


首先來說,委托的作用就是可以給類的方法傳遞其他類的方法而不必將類實例化。第二點,委托就是事件和響應事件的方法的橋梁(就是傳遞響應事件的方法給事件)。這裡要註意,委托跟類平級,事件與方法平級。

         全文

    事件與委托似乎很難以理解,這是因為它們的使用方式與常用的編碼有很大的差別,例如通常編寫的都是同步代碼,調用一個類型的方法,會即刻出現方法執行的結果,這是符合邏輯的。但在某些情況中,同步代碼未必滿足需求,拿公車來打個比方,如果交通管制中心希望每一輛公車到達一個站點時都發送給自己一個信號以便自己能夠隨時掌握交通狀況,使用同步代碼,公汽對象肯定需要調用管制中心對象,這樣就出現了我們一直不願意看到的情況:兩個類型緊密地耦合在一起。既然要其它類型對自己的行為作出反應,親自調用其類型的方法似乎不可避免,在同步代碼中,很難避免這種緊密的類型調用關係。 另一個差別是在一般情況下,我們只將屬性作為參數傳遞給方法,而很少會考慮將一個方法傳遞給另一個方法。 我們拋棄各種C#參考書中桀驁難懂的事件與委托概念,設想一個情景來理解事件與委托的使用:有一家IT公司,董事長不希望自己的雇員在上班時間玩游戲,但又不可能每時每刻都盯著每個雇員,因此,他希望使用一種新的方式實現監視雇員的效果:如果有雇員違反規定,某個設備或專門的監查人員將自動發出一個消息通知他,董事長只需要在事情發生時進行處理。 因此,這個用例實際上是兩種類型——董事長類與雇員類——之間的交互,下麵的代碼將給讀者展示如何使用委托與事件機制實現這種交互: 首先,我們需要在董事長類與雇員類之間定義一個委托類型,用於傳遞兩者之間的事件,這個類型就是一個監視設備或專門負責打小報告的監查人員:

  1. public delegate void DelegateClassHandle();  

定義一個委托的過程類似方法的定義,但它沒有方法體。定義委托一定要添加關鍵字delegate。由於定義委托實際上相當一個類,因此可以在定義類的任何地方定義委托。另外,根據委托的可見性,也可以添加一般的訪問修飾符,如public、private和protected。 委托的返回值類型為void,這並非表示委托類型本身帶有返回值,該返回值類型是指委托的目標函數類型,即它委托的一個事件處理函數返回值是void類型。 新建一個雇員類Employee,其代碼如下:

  1. public class Employee  
  2. {  
  3.     public event DelegateClassHandle PlayGame;  
  4.    
  5.     public void Games()  
  6.     {  
  7.         if (PlayGame != null)  
  8.         {  
  9.             PlayGame();  
  10.         }  
  11.     }  
  12. }  

雇員類Employee代碼中定義了一個DelegateClassHandle類型的事件PlayGame,它的定義方式也很特殊,首先必須使用關鍵字event,表示PlayGame是一個事件,同時還必須聲明該事件的委托類型為DelegateClassHandle,即將來由該類型的委托對象負責通知事件。 如果有雇員開始玩游戲,它將執行Games方法,而只要該方法一被調用,就會觸發一個事件PlayGame,然後董事長就會收到這個事件的消息——有人在玩游戲了。 董事長類代碼如下,他有一個方法Notify用於接收消息:

  1. public class Admin  
  2. {  
  3.     public void Notify()  
  4.     {  
  5.         System.Console.WriteLine("someone is playing game");  
  6.     }  

Employee的PlayGame事件如何與Admin的Notify方法關聯起來呢?只需通過事件綁定即可實現,具體過程如下列代碼:

  1. Employee employee = new Employee();  
  2. Admin admin = new Admin();  
  3.    
  4. employee.PlayGame += new DelegateClassHandle(admin.Notify);  
  5. employee.Games();  

請大家註意事件綁定的代碼:

  1. employee.PlayGame += new DelegateClassHandle(admin.Notify);  

通過DelegateClassHandle將兩個類的交互進行了綁定,當employee.Games方法調用後,觸發PlayGame事件,而該事件將被委托給admin的Notify方法處理,通知董事長有雇員在上班時間玩游戲。 但董事長並不滿足這種簡單的通知,他還想知道究竟是誰在上班時間違反規定。顯然,現在委托對象必須傳遞必要的參數才行,這個要求也可以很容易地辦到。事件的參數可以設置為任何類型的數據,在.NET框架中,還提供了事件參數基類EventArgs專門用於傳遞事件數據。 從該EventArgs類派生一個自定義的事件參數類CustomeEventArgs,這個類型將攜帶雇員姓名和年齡信息:

  1. public class CustomeEvetnArgs : EventArgs  
  2. {  
  3.     string name = "";  
  4.     int age = 0;  
  5.     public CustomeEvetnArgs()  
  6.     { }  
  7.     publicstring Name  
  8.     {  
  9.         get { returnthis.name; }  
  10.         set { this.name = value; }  
  11.     }  
  12.     publicint Age  
  13.     {  
  14.         get { returnthis.age; }  
  15.         set { this.age = value; }  
  16.     }  

修改委托類型DelegateClassHandle的定義,讓其攜帶必要的參數: public delegate void DelegateClassHandle(object sender, CustomeEvetnArgs e); 雇員類的代碼修改後如下:

  1. public class Employee  
  2. {  
  3.     private string _name;  
  4.    
  5.     public string Name  
  6.     {  
  7.         get { return _name; }  
  8.         set { _name = value; }  
  9.     }  
  10.     private int _age;  
  11.    
  12.     public int Age  
  13.     {  
  14.         get { return _age; }  
  15.         set { _age = value; }  
  16.     }  
  17.    
  18.     public event DelegateClassHandle PlayGame;  
  19.    
  20.     public void Games()  
  21.     {  
  22.         if (PlayGame != null)  
  23.         {  
  24.             CustomeEvetnArgs e = new CustomeEvetnArgs();  
  25.             e.Name = this._name ;  
  26.             e.Age = this._age;  
  27.             PlayGame(this, e);  
  28.         }  
  29.     }  
  30. }  

在Games方法中,首先新建一個CustomeEventArgs對象,然後設置了必要的屬性Name和Age。 董事長的通知方法也必須相應地進行修改:

  1. public class Admin  
  2. {  
  3.     public void Notify(object sender, CustomeEvetnArgs e)  
  4.     {  
  5.         System.Console.WriteLine(e.Name+" is "+e.Age.ToString());  
  6.     }  

將兩個類型對象進行關聯的代碼也需要進行相應的修改:

  1. Employee employee = new Employee();  
  2. employee.Name = "Mike";  
  3. employee.Age = 25;  
  4. Admin admin = new Admin();  
  5.    
  6. employee.PlayGame += new DelegateClassHandle(admin.Notify);  
  7. employee.Games();  

修改後的代碼運行的結果是,當Mike調用Games方法玩游戲時,會自動觸發PlayGame事件,而該事件攜帶相關信息通知admin,後者的Notify方法將接收到數據並輸出“Mike is 25”,告訴董事長是Mike,25歲,正在上班時間玩游戲。 委托是可以多路廣播(Mulitcast)的,即一個事件可以委托給多個對象接收並處理。在上面的用例中,如果有另一位經理與董事長具有同樣的癖好,也可以讓委托對象將雇員的PlayGame事件通知他。 首先定義經理類:

  1. public class Manager  
  2. {  
  3.     public void Notify(object sender, CustomeEvetnArgs e)  
  4.     {  
  5.         System.Console.WriteLine(sender.ToString() + "-" + e.Name);  
  6.     }  

經理Manager類型的Notify方法與Admin一致,他也接受到相應的信息。 委托的多路廣播綁定的方法仍然是使用+=運算符,其方法如下麵的代碼所示:

  1. Employee employee = new Employee();  
  2. employee.Name = "Mike";  
  3. employee.Age = 25;  
  4. Admin admin = new Admin();  
  5. Manager manager = new Manager();  
  6.    
  7. employee.PlayGame += new DelegateClassHandle(admin.Notify);  
  8. employee.PlayGame += new DelegateClassHandle(manager.Notify);  
  9. employee.Games(); 

執行該方法,讀者將看到admin和manager的Notify方法都會被事件通知並調用執行。通過這樣的方法,董事長和經理都會知道Mike在玩游戲了。 如果董事長不希望經理也收到這個通知,該如何解除PlayGame對manager的事件綁定呢?同樣非常簡單,在employee.Games方法被調用前執行下列語句即可:

  1. employee.PlayGame -= new DelegateClassHandle(manager.Notify); 

最後需要提醒讀者註意的,Employee類中的Games方法在觸發事件PlayGame之前需要判斷該事件是否為null。當employee對象的 Games方法觸發事件PlayGame後,必須有一個目標函數來處理這個事件,而該語句正是判斷該目標函數是否存在。如果將這個判斷去掉,且對事件不進行任何綁定而直接調用Games方法,程式將在事件PlayGame處彈出一個NullReferenceException的異常。 讀者能夠從委托與事件的代碼中得出什麼結論嗎?兩個需要存在調用關係的類型,在各自的實現中卻沒有編寫實際的調用代碼,它們只是通過一個事件和一個第三方的委托類型完成了消息的傳遞過程。兩個類型之間不存在任何的緊密耦合,它們看似鬆散地通過一個委托對象中通信,實現了本書一直宣傳的“高聚合”和“低耦合”觀點。

  分類: C#課堂
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 因為串口列印速度慢,調試程式會影響程式的真正效果,所以引入網路列印 debug_manager.h #ifndef _DEBUG_MANAGER_H #define _DEBUG_MANAGER_H #define APP_EMERG "" /* system is unusable */ #def... ...
  • I will gradually learn some instructions about Network in Linux. First, here are somethings about IPv4: There are A, B, C, D, E, 5 classes. (images co ...
  • 前段時間換成Mac電腦之後,發現有一點不爽,不能在Mac下寫入NTFS格式的磁碟,所以就去研究了一下。 解決方法有如下三種。 第一種,直接使用第三方軟體,如Paragon NTFS for MAC,Tuxera NTFS等,不過大部分都是收費的。有一款免費的是Mounty,我之前用了一下,我自己的機 ...
  • 代碼如下,實現了Linux系統的一些常見的監控變數,分享給大家@.·.@ ...
  • ...
  • 註:下麵用 [$] 標註的表示收費工具,但部分收費工具針對開源軟體的開發/部署/托管是免費的) 目錄 API 應用框架(Application Frameworks) 應用模板(Application Templates) 人工智慧(Artificial Intelligence) 程式集處理(As ...
  • flex 訪問WebService的方法有很多種,使用FLEX4中的"數據/服務"功能可以自動生成訪問WebService的代理類,這樣可以避免把所有的數據訪問都寫到MXML頁面上,便於重覆利用,同時可以直接導入後臺自定義數據類型,方便傳參。 直接上代碼:其中WebService介面 ? 1 2 3 ...
  • 閱讀目錄 開始 簡單使用 非同步調用WebServeices WebServices驗證 相關概念及資源 WebServices:簡單理解--解決了不同平臺之間應用程式間通信的問題,數據以XML格式在程式間傳輸 實際的應用場景:例如 比價網 為什麼比價網可以從眾多的電商站點獲得用戶搜索的產品數據?We ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...