【C#進階系列】10 事件

来源:http://www.cnblogs.com/vvjiang/archive/2016/03/14/5277535.html
-Advertisement-
Play Games

事件,定義了事件成員的類型允許類型或類型的實例通知其它對象發生了特定的事情。 按照我自己的理解而言,事件可以被(方法)關註,也可以被(方法)取消關註,事件發生後關註了事件的一方會瞭解到,並對事件做出相應的應對(執行方法)。(我每次都是這麼理解的,這樣從字面意義上更好理解一點) 眾所周知,事件實際上就


事件,定義了事件成員的類型允許類型或類型的實例通知其它對象發生了特定的事情。

按照我自己的理解而言,事件可以被(方法)關註,也可以被(方法)取消關註,事件發生後關註了事件的一方會瞭解到,並對事件做出相應的應對(執行方法)。(我每次都是這麼理解的,這樣從字面意義上更好理解一點)

眾所周知,事件實際上就是基於委托的。而委托是調用回調函數的一種類型安全的方式。

今天寫一個關於分手的事件Demo,算是生動形象吧。

定義事件參數類(可忽略這步)

一個事件發生後若要傳遞附加的參數信息,就需要定義事件參數類,需要繼承EventArgs,否則就直接使用EventArgs.Empty即可。(EventArgs.Empty實際上就是new EventArgs())

     public class 分手EventArgs : EventArgs
        {
            public 分手EventArgs(string name, string title)
            {
                this._分手的人 = name;
                this._分手原因 = title;
            }
            public string 分手的人
            {
                get
                {
                    return _分手的人;
                }
            }
            public string 分手原因
            {
                get
                {
                    return _分手原因;
                }
            }
            private readonly string _分手的人;
            private readonly string _分手原因;
        }

定義事件成員

     public class Troy {
            //委托類型EventHandler<T>的聲明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
            //第一個對象顧名思義是事件發出者,這裡肯定就是Troy發生了事件
            //第二個參數傳遞的就是我們之前定義的事件附加信息
            public event EventHandler<分手EventArgs> 宣稱要分手;
        }

現在有了一個Troy的宣稱分手事件。

既然事件有了,那麼接下來就是去讓本人去引發這個事件

引發事件

於是就變成了這樣

    public class Troy {
            //委托類型EventHandler<T>的聲明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
            //第一個對象顧名思義是事件發出者,這裡肯定就是Troy發生了事件
            //第二個參數傳遞的就是我們之前定義的事件附加信息
            public event EventHandler<分手EventArgs> 宣稱要分手;
            //定義負責引發事件的方法來通知已關註事件的人,一般是要定義為protected和virtual
            protected virtual void On宣稱要分手(分手EventArgs e) {
                //如果有人聽我說這個事,那麼就說,沒人聽我就肯定不說了,你在程式里玩自言自語也不是不行,不過顯得很傻而已(完美貼切好形象)
                if(宣稱要分手!=null)this.宣稱要分手(this,e);
            }
        }

本書中其實還介紹了一個出於線程安全考慮的引發事件的寫法,

因為如果在我宣稱要分手前,本來聽我講這個事的人A有另一個人B叫他,A他突然跑掉了(在另一個線程中 宣稱要分手 的委托鏈就被移除了委托),那麼此時 宣稱要分手 這個事件就沒人聽了(宣稱要分手為null),然後我醞釀了半天的話吐不出來就報了個Null異常。

所以有了下麵這種寫法

        protected virtual void On宣稱要分手(分手EventArgs e) {
                //下麵代碼的意思就是:我要說分手的時候將關註了 宣稱要分手 這個事件的人都拉到討論組中
                //然後就算被另一個線程的人將在我旁邊的人都叫走了,實際上因為我把他們丟拉到討論組中了
                //此時討論組中都有成員,所以關註的人還是獲悉了這個悲傷的故事
                var 討論組 = System.Threading.Volatile.Read(ref 宣稱要分手);//Volatile.Read僅僅起到賦值宣稱要分手的引用給討論組,之所以不用等於,是因為可能會被編譯器優化時去掉臨時變數 討論組。
                if (討論組 != null)this.宣稱要分手(this,e);
            }

然而JIT編譯器表示,實際上這種用等於也可以,只是為了防範於未然。

然而此處我只想安靜地分手,所以讓我們沿用更上面的說法

真實的引發事件

我每天都可以說很多分手事件,有的是你,有的還是你。

為了更準確的把這個事給說清楚了,我也許還要傳遞個信息,就是這次可能要分手的是我。

    public class Troy {
            //委托類型EventHandler<T>的聲明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
            //第一個對象顧名思義是事件發出者,這裡肯定就是Troy發生了事件
            //第二個參數傳遞的就是我們之前定義的事件附加信息
            public event EventHandler<分手EventArgs> 宣稱要分手;
            //定義負責引發事件的方法來通知已關註事件的人,一般是要定義為protected和virtual,前者是要,後者是為了派生類的必要
            protected virtual void On宣稱要分手(分手EventArgs e) {
                if(宣稱要分手!=null)this.宣稱要分手(this,e);
            }
            public void 宣稱自己要分手() {
                //這一步我們把事說清楚
                var e = new 分手EventArgs("Troy", "心累");
                On宣稱要分手(e);//如果子類沒有重寫這個事件引發函數,那麼就告訴所有關註的人這個事
            }
        }

事件類型被編譯後會出現什麼?

C#編譯器將事件編譯後會轉換成3個東西:一個私有的委托欄位 宣稱要分手,一個公共的關註事件的方法 add_宣稱要分手,一個公共的取消關註的方法 remove_宣稱要分手。

除了這三個東西,編譯器還會在托管程式集的元數據中生成一個事件定義記錄項,它的作用只是為了建立“事件”的抽象概念和它的訪問器方法之間的聯繫。(高深吧?然而你並不需要懂)

關於關註事件的那些人

事件以及事件的引發都弄好了,接下來就是定義關註事件的那些人了。以下人物由真實人物改編:

     //下麵是將婚同事李
        public class Lee {
            public Lee(Troy troy) {
                //初始化就關註事件
                troy.宣稱要分手 += this.AfterListen;
            }
            private void AfterListen(Object sender, 分手EventArgs e) {
                Console.WriteLine("讓troy去玩游戲");
            }
        }
        //然後是單身同事肖
        public class Xiao {
            public Xiao(Troy troy)
            {
                troy.宣稱要分手 += this.AfterListen;
            }
            private void AfterListen(Object sender, 分手EventArgs e)
            {
                Console.WriteLine("表示Troy打擊單身狗");            }
            //由於Xiao並不是每天都和Lee一樣,與Troy同行,所以Xiao也許開始聽得到,後來跑遠了,就取消關註事件了
            private void UnListen(Troy troy) {
                troy.宣稱要分手 -= this.AfterListen;
            }
        }

一個對象只要某個方法關註了事件,那麼它就不能被回收了。所以如果你想讓垃圾處理器回收某對象,就讓他不要再關註事件了。

好吧,就這些了。

PS:

這確實是一個真實的故事,就在昨天。

我也不知道我為什麼還有心情寫博客,反正除了這個我也已經不知道該幹嘛好了。

大概是因為就算失戀了也不可能說不去吃飯睡覺之類的感覺吧。

更加令人驚奇的是,效率不知道為什麼出奇得高,以至於十一點前就完成了學習和博客。

每個人都需要去成長,成長的故事大多都是不舒服的,一如熬夜學習寫博客,一如分手這件事。

懷著感恩的心去面對已經失去的人,反而比什麼樣的療傷都來得有效d(╯﹏╰)b


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

-Advertisement-
Play Games
更多相關文章
  • 負責維護公司產品的web伺服器搭建與維護,最近遇到一下狀況,今天在這裡簡單總結一下,希望對於剛剛一些剛入行的小伙伴有所幫助,避免再走彎路。 第一點:Tomcat記憶體設置: 一、常見的Java記憶體溢出有以下三種: 1. java.lang.OutOfMemoryError: Java heap spa
  •   線纜作為連接器件,相當於不同系統之間溝通的“橋梁”,選擇線纜類型的好壞,也決定著傳輸信號的質量,影響著整個系統的穩定性。 (1)特性阻抗   先說一下關於線纜在傳輸過程中的特性阻抗問題。   特性阻抗是指電纜無限長時該電纜所具有的阻抗,阻抗是阻止交流電流通的一種電阻,(所以萬用表測不能直接測出一
  •  
  •  
  • 最後要用一方法判斷ip地址是否正確,直接用.Net現成的類,方法如下:
  • 某一時候,為文本框(TextBox)裝飾個水印。它有兩種狀態,一是blur和focus。因此,我們可以在Javascript寫兩個事件: 演示:    
  • 【問】 在C#和Visual Basic的轉換中,以下一些轉換的用法和區別是什麼呢? [C#] [VB.NET] 【錯誤回答】 沒有區別,因為運行了之後都可以正常轉化。 【正解】 光從運行結果來看當然是毫無區別,因為題目所給出的僅僅是一部分的例子,不是全部。許多初學者容易產生“以偏概全”的錯誤認識。
  • 我希望能理解在瀏覽器輸入URL並敲擊回車來請求一個ASP.NET MVC網站的頁面之後發生的任何事情。 為什麼需要關心這些?有兩個原因。首先是因為ASP.NET MVC是一個擴展性非常強的框架。例如,我們可以插入不同的ViewEngine來控制網站內容呈現的方式。我們還可以定義控制器生成和分配到某個
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...