徹底弄懂C#中delegate、event、EventHandler、Action、Func的使用和區別

来源:https://www.cnblogs.com/digital-college/archive/2023/04/03/17282032.html
-Advertisement-
Play Games

【目錄】 1 委托 2 事件-概念的引出 3 事件-關於異常 4 事件-關於非同步 5 委托-Func與Action 1 委托 在.NET中定義“委托”需要用到delegate關鍵字,它是存有對某個方法的引用的一種引用類型變數,類似於 C 或 C++ 中函數的指針。“委托”主要有兩大作用: (1)將方 ...


【目錄】

1 委托

2 事件-概念的引出

3 事件-關於異常

4 事件-關於非同步

5 委托-Func與Action

 

1 委托

在.NET中定義“委托”需要用到delegate關鍵字,它是存有對某個方法的引用的一種引用類型變數,類似於 C 或 C++ 中函數的指針。“委托”主要有兩大作用:

(1)將方法當作參數傳遞

(2)方法的一種多態(類似於一個方法模板,可以匹配很多個方法)

下麵,給出一個展現了上述兩大作用的委托代碼示例:

        //定義一個委托
        public delegate int MyDelegate(int x, int y);

        //與委托匹配的一個方法
        public static int Add(int a, int b)
        {
            return a + b;
        }

        //與委托匹配的另一個方法
        public static int Reduce(int a, int b)
        {
            return a - b;
        }

        //示例:將委托/方法當參數傳遞
        public static int Test(MyDelegate MD)
        {
            return MD(10, 20);
        }

        static void Main(string[] args)
        {
            int a, b, x, y;

            MyDelegate md;

            //將委托指向Add這個方法,併進行相關操作
            md = Add;
            a = md(1, 2);
            b = Test(md);

            //再將委托指向Reduce這個方法,併進行相關操作
            md = Reduce;
            x = md(7, 2);
            y = Test(md);

            Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
            Console.ReadLine();
        }

執行以上程式,輸出結果如下:

1+2=3,10+20=30,7-2=5,10-20=-10

委托也可以使用+=/-=來實現“發佈/訂閱”模式,示例代碼如下:

        //定義一個委托
        public delegate void MyDelegate1(int x);

        public static void Method1(int a)
        {
            Console.WriteLine($"a={a}");
        }

        public static void Method2(int b)
        {
            Console.WriteLine($"b={b}");
        }

        static void Main(string[] args)
        {
            MyDelegate1 md = null;
            md += Method1;
            md += Method2;
            md(35);

            Console.ReadLine();
        }

以上程式輸出如下:

a=35

b=35

但是委托有一個弊端,它可以使用“=”將所有已經訂閱的取消,只保留=後的這一個訂閱。

為瞭解決這個弊端,事件event應運而生。

 

2 事件-概念的引出

事件event是一種特殊的委托,它只能+=,-=,不能直接用=。

event在定義類中(發佈者)是可以直接=的,但是在其他類中(訂閱者)就只能+= -=了,也就是說發佈者發佈一個事件後,訂閱者針對他只能進行自身的訂閱和取消。

下麵是定義一個事件的代碼:

        //定義一個委托
        public delegate void MyDelegate1(int x);
        //定義一個事件
        public event MyDelegate1 emd;

經過長久的經驗積累後,人們發現,絕大多數事件的定義,是用public delegate void XXX(object sender, EventArgs e);這樣一個委托原型進行定義的,是一件重覆性的工作,於是,EventHandler應運而生。它的出現就是為了避免這種重覆性工作,並建議儘量使用該類型作為事件的原型。

//@sender: 引發事件的對象
//@e: 傳遞的參數
public delegate void EventHandler(object sender, EventArgs e);

//使用
public event EventHandler emd;


下麵給出一個使用事件的具體示例:

        public class Demo
        {
            public event EventHandler emd;
            public void RaiseEvent()
            {
                emd(this, EventArgs.Empty);
            }
        }

        static void Main(string[] args)
        {
            var instance = new Demo();
            instance.emd += (sender, arg) =>
            {
                Console.WriteLine("執行事件1!");
            };

            instance.emd += (sender, arg) =>
            {
                Console.WriteLine("執行事件2!");
            };

            instance.RaiseEvent();

            Console.ReadLine();
        }

這裡我們先定義一個Demo類,其內部有個事件是emd,我們給他開放了一個介面RaiseEvent,如果誰敢調用它,那麼,它就觸發報警事件emd。

這裡模擬了2個訂閱者,分別處理報警事件emd。

程式執行結果如下:

執行事件1!

執行事件2!

同時,我們也可以看出:事件是按照+=的訂閱先後順序執行的。

3 事件-關於異常

現在,我們在第一個訂閱者中加入異常,如下:

    instance.emd += (sender, arg) =>
    {
        Console.WriteLine("執行事件1!");
        throw new Exception("執行事件1,錯誤");
    };

 

執行後發現,第1個訂閱者事件觸發拋出異常後,第2個訂閱者的事件沒有執行。

可見,如果你想讓所有訂閱者都執行處理的話,那每個訂閱者必須在訂閱程式內自己處理好異常,不能拋出來!

 

4 事件-關於非同步

如果事件的訂閱者中有一個是“非同步”處理,又會是什麼情況?

下麵我們把第1個訂閱者改為非同步處理,代碼如下:

    instance.emd += async (sender, arg) =>
    {
        Console.WriteLine("執行事件1!");
        await Task.Delay(1000);
        Console.WriteLine("執行事件1!完畢");
    };

執行後輸出如下:
執行事件1!

執行事件2!

執行事件1!完畢

可見,非同步的事件處理沒有阻塞進程,很好的起到了非同步方法的作用。

 

5 委托-Func與Action

本文最開始探討委托,然後直接順到了事件的相關話題上。其實,關於委托還有一個重點話題漏掉了,那就是Func與Action。

在委托delegate出現了很久以後,微軟的.NET設計者們終於領悟到,其實所有的委托定義都可以歸納並簡化成只用Func與Action這兩個語法糖來表示。其中,Func代表有返回值的委托,Action代表無返回值的委托。有了它們兩,我們以後就不再需要用關鍵字delegate來定義委托了。

同時,若再用lambda表達式取代被委托指向的具體方法,則整個委托的“定義+賦值”兩步將大大簡化(lambda表達式本來也是方法定義的一種簡化形式)。

下麵,把最開始委托章節中關於加減法的程式代碼,用Func與lambda表達式進行簡化改造,改造後的代碼如下:

        //示例:將委托/方法當參數傳遞
        public static int Test(Func<int, int, int> MD)
        {
            return MD(10, 20);
        }

        static void Main(string[] args)
        {
            int a, b, x, y;

            Func<int, int, int> md;

            //將委托指向加法,併進行相關操作
            md = (t, v) => t + v;
            a = md(1, 2);
            b = Test(md);

            //再將委托指向減法,併進行相關操作
            md = (t, v) => t - v;
            x = md(7, 2);
            y = Test(md);

            Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
            Console.ReadLine();
        }

是不是代碼大大簡化了?簡化了哪些內容,你可以前後對比一下...(本文完)

 


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

-Advertisement-
Play Games
更多相關文章
  • 摘要:本次案例,用定值Cookie實現反爬。 本文分享自華為雲社區《我是怎麼用一個特殊Cookie,限制住別人的爬蟲的》,作者: 夢想橡皮擦 。 Cookie 生成 由於本案例需要用到一個特定的 Cookie ,所以我們需要提前將其生成,你可以直接設置一個固定的字元串,也可以使用 Python 加密 ...
  • 一、問題引入 圖書信息管理系統: 出版社有一些圖書數據保存在一個文本文件book.txt 中,為簡單起見,在此假設每種圖書只包括三部分信息:ISBN (書號)、書名和價格,文件中的部分數據如圖2.1 所示。現要求實現一個圖書信息管理系統,包括以下6個具體功能。 (1) 查找:根據指定的ISBN 或書 ...
  • 什麼是SwaggerHub? Hub 謂之 中心, 所以 SwaggerHub即swagger中心. 什麼時候需要它? 通常, 公司都擁有多個服務, 例如商品服務, 訂單服務, 用戶服務, 等等, 每個服務都有自己的environment, endpoint, swagger schema. 然而這 ...
  • 首先,安裝AspNetCore.RateLimit NuGet 包。您可以通過NuGet包管理器控制台或Visual Studio的NuGet包管理器來執行此操作。安裝後,您將在項目中看到一個名為AspNetCoreRateLimit的文件夾,其中包含中間件的配置類。 接下來,您需要在 Startu ...
  • //裝箱拆箱 string name = "Zery"; int age = 22; Console.WriteLine(age.ToString() + name);//已ToString的操作 Console.WriteLine(age+name);//未ToString操作 老規矩,一段C#, ...
  • 閱讀IL主要是為了能夠更好的學會Emit 從控制台開始吧:事先準備工具ILSpy,和IL的命令指南(這個可以網上搜索或者去看OpCode枚舉),記住棧中的都是引用的地址 int i = 10; int j = 20; int k = 30; Console.WriteLine(i + j + k); ...
  • #1. 緩存 緩存指的是在軟體應用運行過程中,將一些數據生成副本直接進行存取,而不是從原始源(資料庫,業務邏輯計算等)讀取數據,減少生成內容所需的工作,從而顯著提高應用的性能和可伸縮性,使用好緩存技術,有利於提高我們提升用戶體驗性。 對於緩存的使用有以下一些註意點: 緩存最適用於不常更改且生成成本很 ...
  • 1.創建一個新的WinForms或WPF應用程式,具體取決於您的需要。 2.將Telerik Reporting組件添加到您的應用程式中。您可以通過NuGet包管理器來完成此操作。 3.在您的應用程式中添加多個報表文件。您可以使用Telerik Report Designer創建報表並將其添加到您的 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...