每個.NET開發都應掌握的C#委托事件知識點

来源:https://www.cnblogs.com/xbhp/archive/2023/08/21/17645729.html
-Advertisement-
Play Games

上篇文章講述了[C#介面的知識點](https://mp.weixin.qq.com/s?__biz=MzI2NDE1MDE1MQ==&mid=2650851371&idx=1&sn=c630043f8d85816c660c53cbf6b3f218&chksm=f14565c3c632ecd5816 ...


上篇文章講述了C#介面的知識點,本文將介紹C#委托事件知識點。C#作為.NET開發的核心語言之一,提供了豐富的特性來支持面向對象編程和事件驅動的模型。其中,委托和事件是C#中不可或缺的關鍵概念,每個.NET開發者都應該深入理解它們的作用和用法。委托和事件密不可分,所以本文將委托和事件的知識點一起介紹,並通過一些示例來幫助開發者更好地掌握這些重要的概念。

一、委托

委托讓方法引用的靈活利用

1、委托的定義與使用

委托是一種數據類型,用於持有對一個或多個方法的引用。通過委托,你可以將方法作為參數傳遞給其他方法,實現回調機制,實現方法的動態調用。使用`delegate`關鍵字可以聲明委托類型,並創建委托實例來綁定具體方法。

using System;
//定義一個委托
delegate int CalculatorDelegate(int a, int b);
class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    public int Subtract(int a, int b)
    {
        return a - b;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Calculator calculator = new Calculator();
        // 聲明委托實例,並綁定到 Add 方法
        CalculatorDelegate funDelegate = new CalculatorDelegate(calculator.Add);
        // 使用委托調用方法
        int result = funDelegate (5, 2);
        Console.WriteLine($"5 + 2 = {result}");
        // 將委托重新綁定到 Subtract 方法
        funDelegate = calculator.Subtract;
        // 使用委托調用不同的方法
        result = funDelegate(8, 6);
        Console.WriteLine($"8 - 6 = {result}");
    }
}

使用try.dot.net的測試結果:

圖片

2、委托的多播

委托不僅可以持有單個方法的引用,還可以用於多播,即將多個方法綁定到同一個委托實例。多播委托允許按順序調用這些方法,實現一次觸發多個方法的功能。

using System;
delegate void MyDelegate(); // 定義委托
class EventPublisher
{
    private MyDelegate eventHandlers; // 多播委托實例
    public void AddHandler(MyDelegate handler)
    {
        eventHandlers += handler; // 添加委托方法到多播鏈
    }
    public void RemoveHandler(MyDelegate handler)
    {
        eventHandlers -= handler; // 從多播鏈中移除委托方法
    }
    public void RaiseEvent()
    {
        Console.WriteLine("事件發佈者正在引發事件...");
        eventHandlers?.Invoke(); // 調用多播鏈中的委托方法
    }
}
class Program
{
    static void Main(string[] args)
    {
        EventPublisher publisher = new EventPublisher();
        // 添加多個事件處理程式到多播鏈
        publisher.AddHandler(Method1);
        publisher.AddHandler(Method2);
        publisher.AddHandler(Method3);
        // 觸發事件,調用多播鏈中的所有方法
        publisher.RaiseEvent();
        // 移除一個事件處理程式
        publisher.RemoveHandler(Method2);
        // 再次觸發事件
        publisher.RaiseEvent();
        Console.ReadKey();
    }
    static void Method1()
    {
        Console.WriteLine("方法1運行.");
    }
    static void Method2()
    {
        Console.WriteLine("方法2運行.");
    }
    static void Method3()
    {
        Console.WriteLine("方法3運行.");
    }
}

輸出結果:

圖片

3、 匿名方法與Lambda表達式

C# 2.0 引入了匿名方法,允許在沒有顯示聲明方法的情況下傳遞代碼塊作為委托參數。而Lambda表達式則是C# 3.0 的新特性,提供了更簡潔的語法來創建委托實例。.NET的ORM框架EF中有了Lambda表達式方便多了。

進化:委托-->匿名方法-->Lambda

案例:下麵案例是委托匿名方法和Lambda表達式三種使用案例

using System;
using System.Linq;
delegate int MathOperation(int a, int b);
class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    public int Subtract(int a, int b)
    {
        return a - b;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Calculator calculator = new Calculator();
        // 1、使用委托和實例方法 Add
        MathOperation operation1 = calculator.Add;
        int result1 = operation1(10, 5);
        Console.WriteLine($"用委托方法計算: 10 + 5 = {result1}");
        // 2、使用匿名方法進行減法運算
        //MathOperation  operation2 = delegate (int a, int b)
        Func<int,int,int> operation2 = delegate (int a, int b)//內置Func
        {
            return a - b;
        };
        int result2 = operation2(15, 7);
        Console.WriteLine($"用匿名方法計算: 15 - 7 = {result2}");
        // 3、使用 Lambda 表達式進行乘法運算
        //MathOperation operation3 = (a, b) => a * b;//委托變數
        Func<int,int,int> operation3 = (a, b) => a * b;//內置Func
        int result3 = operation3(8, 6);
        Console.WriteLine($"用Lambda計算: 8 * 6 = {result3}");
        Console.ReadKey();
    }
}
//歡迎關註公眾號:DOTNET開發跳槽,領取海量面試題。
//加微信號xbhpnet入群交流.NET求職和技術

效果如下:

圖片

4、委托的BeginInvoke方法實現非同步

委托的 BeginInvoke 方法和 EndInvoke 方法可以實現非同步執行委托方法。這允許委托的方法在後臺線程中執行,而不會阻塞當前線程。小編在之前的webform開發中遇到下載進度條卡死的問題就是用它解決的。

案例:

using System;
using System.Threading;

delegate void PrintDelegate(string message);
class Program
{
    static void PrintMessage(string message)
    {
        for (int i = 0; i < 10000; i++)
        {
            Console.WriteLine(message);
        }
    }
    static void Main(string[] args)
    {
        PrintDelegate print = PrintMessage;
        // 使用委托的 BeginInvoke 方法來非同步執行方法
        IAsyncResult result = print.BeginInvoke("執行非同步方法!", null, null);
        // 使用委托的 EndInvoke 方法獲取非同步操作結果
        print.EndInvoke(result);//這裡不會卡死
        Console.WriteLine("Begin 後的方法");
        Console.ReadKey();
    }
}
//由於控制台不支持展示,大家可以自己研究一下。

二、事件

事件對象之間的松耦合通信

1、事件的定義與聲明

事件是委托的一種特殊應用,用於實現發佈-訂閱模型。使用event關鍵字可以聲明事件,並指定事件委托的類型。事件允許對象通知其他對象在特定情況下執行操作,實現松耦合的通信機制。

 //聲名
 public event TemperatureChangeHandler TemperatureChanged;

2、事件的訂閱與發佈

訂閱事件的類(事件訂閱者)可以將其方法綁定到事件上,以便在事件觸發時執行操作。事件的持有者(事件發佈者)在適當的時機觸發事件,調用事件委托,從而通知所有訂閱者執行相應的操作。

案例:

using System;

// 定義事件發佈者類
class EventPublisher
{
    // 聲明事件委托
    public event EventHandler<string> MessageSent;
    // 觸發事件的方法
    public void SendMessage(string message)
    {
        Console.WriteLine($"發送消息:{message}");
        OnMessageSent(message);
    }
    // 觸發事件的保護方法
    protected virtual void OnMessageSent(string message)
    {
        MessageSent?.Invoke(this, message); // 調用事件
    }
}

// 定義事件訂閱者類
class EventSubscriber
{
    public void Subscribe(EventPublisher publisher)
    {
        // 訂閱事件
        publisher.MessageSent += HandleMessageSent;
    }
    public void Unsubscribe(EventPublisher publisher)
    {
        // 取消訂閱事件
        publisher.MessageSent -= HandleMessageSent;
    }
    // 事件處理方法
    private void HandleMessageSent(object sender, string message)
    {
        Console.WriteLine($"接收到消息:{message}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        EventPublisher publisher = new EventPublisher();
        EventSubscriber subscriber1 = new EventSubscriber();
        EventSubscriber subscriber2 = new EventSubscriber();
        // 訂閱事件
        subscriber1.Subscribe(publisher);
        subscriber2.Subscribe(publisher);
        // 發佈事件
        publisher.SendMessage("你好,訂閱者們!");
        Console.WriteLine("取消一個訂閱者的訂閱...");
        // 取消訂閱一個訂閱者
        subscriber1.Unsubscribe(publisher);
        // 發佈事件
        publisher.SendMessage("再次打個招呼!");

        Console.ReadKey();
    }
}

輸出:

圖片

3、事件的安全性與封裝

事件提供了一種封裝機制,使得事件只能被持有者觸發,而不會被外部隨意調用。這樣可以確保事件只在控制的範圍內使用,增強代碼的安全性和可維護性。

三、委托與事件的關係

事件是委托的一種特殊用法,用於實現發佈者/訂閱者模式,實現對象之間的松耦合通信。委托是一種通用的類型,用於引用方法並執行它們,而事件是委托的一種實現,允許對象訂閱和響應特定情況的通知,從而促進模塊化和可維護的代碼設計。通過事件,對象可以在不直接依賴於其他對象的情況下,將重要信息傳遞給感興趣的觀察者。

下麵將用一個案例來理解委托和事件

為了更好地理解委托和事件,我們可以以一個簡單的溫度監測系統為例。假設有一個溫度監測器對象,當溫度發生變化時,它可以通知其他對象執行相應的操作。

using System;
// 定義一個委托,用於處理溫度變化事件
delegate void TemperatureChangeHandler(double temperature);
// 溫度監控類
class TemperatureMonitor
{
    // 定義事件,將委托作為事件處理程式
    public event TemperatureChangeHandler TemperatureChanged;
    private double currentTemperature; // 當前溫度
    // 屬性,獲取和設置當前溫度,當溫度發生變化時觸發事件
    public double CurrentTemperature
    {
        get { return currentTemperature; }
        set
        {
            if (value != currentTemperature)
            {
                currentTemperature = value;
                OnTemperatureChanged(value); // 溫度變化時調用事件
            }
        }
    }
    // 觸發溫度變化事件的方法
    protected virtual void OnTemperatureChanged(double temperature)
    {
        TemperatureChanged?.Invoke(temperature); // 調用事件處理程式
    }
}
class Program
{
    static void Main(string[] args)
    {
        TemperatureMonitor monitor = new TemperatureMonitor();
        // 訂閱溫度變化事件,將方法 OnTemperatureChanged 作為事件處理程式
        monitor.TemperatureChanged += OnTemperatureChanged;
        // 改變當前溫度,觸發事件
        monitor.CurrentTemperature = 25.5;
        Console.ReadKey();
    }
    // 溫度變化事件處理程式
    static void OnTemperatureChanged(double temperature)
    {
        Console.WriteLine($"溫度變化 {temperature}°C");
    }
}
//案例參考:C#委托事件-張子陽

效果如下:

圖片

以上代碼示例使用了委托和事件,實現了觀察者模式。觀察者模式是一種行為設計模式,它定義了對象之間的一對多依賴關係,使得當一個對象的狀態發生變化時,所有依賴於它的對象都會得到通知並自動更新。在這個示例中,TemperatureMonitor 類充當了被觀察者(發佈者),Program 類中的 OnTemperatureChanged 方法充當觀察者(訂閱者)

結語

委托和事件是C#中的重要概念,在C#中無論是實現回調機制、處理非同步操作,還是實現事件驅動的架構,委托和事件都是不可缺的,每個.NET開發者都應該深入瞭解和熟練掌握。本文只列出了部分基礎知識點,更多知識點大家可以到官網查詢。

希望本文對你有所收穫,對於C#委托和事件的知識點,你還知道哪些?歡迎留言討論或者吐槽本文。

參考:

1、chatgpt

2、微軟官方文檔 :

委托:learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/delegates/

事件:learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/events/

來源公眾號:DotNet開發跳槽

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

-Advertisement-
Play Games
更多相關文章
  • ### 1、% - 運算符 %表示取模運算,也就是取餘數。 例如 6 % 4 = 2 ### 2、% - 引導符/占位符 引導符用於控制輸入輸出的格式。常見於printf("%d",a);scanf("%d",&a);語句。 1) %s - 字元串 (String) 2) %c - 字元 (Char ...
  • 我們希望將這些rpc結果數據緩存起來,併在一定時間後自動刪除,以實現在一定時間後獲取到最新數據。類似Redis的過期時間。本文是我的調研步驟和開發過程。 ...
  • 根節點枚舉的過程要做到高效並非一件容易的事情,現在Java應用越做越龐大,如果你是JVM的開發者,你會怎麼去做? ...
  • `gosec` 是一個用於在 Go 代碼中查找安全問題的開源工具,它可以幫助發現可能的漏洞和潛在的安全風險。以下是關於 `gosec` 的詳細介紹: ## 1. 工具概述: `gosec` 是一個靜態分析工具,用於掃描 Go 代碼以查找潛在的安全問題。它可以識別常見的代碼漏洞、敏感信息泄露和其他安全 ...
  • # What is Polymorphism 這個多態看中文確實有點費解,多態的英文是Polymorphism,它的翻譯含義是: n. 多態性 (可以看出是比較寬泛的) n. 多型現象 從翻譯也看不出啥, 我舉一個生活中的例子來引入多態: 生活中有很多常見的物體具有多態性。例如,一張紙可以用來寫字、 ...
  • # 個人博客-給文章添加上標簽 # 優化計劃 - [x] 置頂3個且可滾動或切換 - [x] 推薦改為4個,然後新增歷史文章,將推薦的載入更多放入歷史文章,按文章發佈時間降序排列。 - [x] 標簽功能,可以為文章貼上標簽 - [ ] 推薦點贊功能 本篇文章實現文章標簽功能 # 思路 > 首先需要新 ...
  • # .NET Evolve資料庫版本管理工具 ## 1.簡介 提到資料庫版本管理,`Java`領域開發首先會想到大名鼎鼎的`flyway`。但是它不適用`.NET`領域,那麼`.NET`領域也需要做資料庫版本管理,該用什麼工具?自行造輪子?`.NET`領域的解決方案就是`Evolve`,這是一個開源 ...
  • # Ocelot與路由共存 ### 引言 在Asp.Net Core中使用了Ocelot做網關之後,其自身的Api路由就不起作用了,尋了許久的解決方法,終於找到一個,主要是使用MapWhen判斷Ocelot的配置是否符合,是則走轉發路由,否則走自身路由,步驟如下: ### 1.先創建以下類 ``` ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...