面試常考:C# 委托(delegate、Action、Func、predicate)和事件

来源:https://www.cnblogs.com/chenhanhans/archive/2023/07/23/17575050.html
-Advertisement-
Play Games

# 面試常考:C# 委托(delegate、Action、Func、predicate)和事件 剛開始工作的時候,覺得委托和事件有些神秘,而當你理解他們之後,也覺得好像沒有想象中的那麼難,這篇博文算是自己對委托和事件的一次梳理和總結。 ## 二、委托 C#中的委托,相當於C++中的指針函數,但委托是 ...


面試常考:C# 委托(delegate、Action、Func、predicate)和事件

剛開始工作的時候,覺得委托和事件有些神秘,而當你理解他們之後,也覺得好像沒有想象中的那麼難,這篇博文算是自己對委托和事件的一次梳理和總結。

二、委托

C#中的委托,相當於C++中的指針函數,但委托是面向對象的,是安全的,是一個特殊的類,當然他也是引用類型,委托傳遞的是對方法的引用。

2.1、delegate

聲明委托就必須使用關鍵字“delegate”,委托是先聲明,後實例化。至少0個參數,至多32個參數

格式如下所示:

private delegate string GetAsString();

委托是一個類,所以他的實例化跟類的實例化一樣,只是他總是接受一個將委托方法作為參數的構造函數。調用委托方法就有兩種方式,如下所示:

int i = 10;  
var method = new GetAsString(i.ToString);  
//調用方法一  
Console.WriteLine($"method方法{method()}");  
//調用方法二  
Console.WriteLine($"method.Invoke方法{method.Invoke()}");  

運行結果:

image-20230723114808765

2.2、Action

Action是無返回值的泛型委托,可以接受0個至16個傳入參數

Action 表示無參,無返回值的委托

Action<int,string> 表示有傳入參數int,string無返回值的委托

前面我們【Log4Net 日誌記錄的實現】中,就使用了Action。如:

public static void Debug(string message, Action RegistedProperties)  
{  
	RegistedProperties();  
	log.Debug(message);  
}

調用方式為:

PFTLog.Debug("測試擴展欄位", () => {  
    LogicalThreadContext.Properties["LogType"] = "擴展欄位內容";  
});  

在運行中,直接運行Action中的內容即可。

2.3、Func

Func是有返回值的泛型委托,可以接受0個至16個傳入參數

Func 表示無參,返回值為int的委托

Func<object,string,int> 表示傳入參數為object, string 返回值為int的委托

public static decimal GetTotal(Func<int, int, decimal> func, int a, int b)  
{  
    return func(a, b);  
}  

調用方式

var total = GetTotal((a, b) => { return (decimal)a + b; }, 1, 2);  
Console.WriteLine($"結果為{total}");  

運行結果

oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP1RuI6pHsmUjDB7O6oiaic18dL5OR6PDibbbKooePBsniappvuaaRpxcn6QQ

2.4、predicate

predicate 是返回bool型的泛型委托,只能接受一個傳入參數

predicate 表示傳入參數為int 返回bool的委托

定義一個方法:

public static bool FindPoints(int a)  
{  
    return a >= 60;  
}

定義Predicate委托

 Predicate<int> predicate = FindPoints;

調用

var points = new int[] {  
    10,  
    50,  
    60,  
    80,  
    100 };  
var result = Array.FindAll(points, predicate);  
Console.WriteLine($"結果為{string.Join(";", result)}");

運行結果

oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP1wZIms9F2kZibE0vfWGYjNc8uaicPY5ibECsrTWU52YovVEHEhDLlusCZA

2.5、多播委托

前面的只包含了一個方法的調用,委托可以包含多個方法,這種委托就叫做多播委托。多播委托利用“+=”和“-+”兩種運算符進行添加和刪除委托。

先定義兩個方法

public static void MultiplyByTwo(double v)  
{  
    double result = v * 2;  
    Console.WriteLine($"傳值:{v};MultiplyByTwo結果為{result}");  
}  
public static void Square(double v)  
{  
    double result = v * v;  
    Console.WriteLine($"傳值:{v};Square結果為{result}");  
}

然後調用

Action<double> operations = MultiplyByTwo;  
operations(1);  
operations += Square;  
operations(2);  

運行結果:

oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP1YbOpyIiaW9FRgaoQZ3OOD7kTlILFqlKl6DNRiavyafZ2InbKm7ibNH3Kw

三、事件

事件是基於委托,為委托提供一種發佈/訂閱機制,聲明事件需要使用event關鍵字。

發佈者(Publisher):一個事件的發行者,也稱作是發送者(sender),其實就是個對象,這個對象會自行維護本身的狀態信息,當本身狀態信息變動時,便觸發一個事件,並通知說有的事件訂閱者;

訂閱者(Subscriber):對事件感興趣的對象,也稱為Receiver,可以註冊感興趣的事件,在事件發行者觸發一個事件後,會自動執行這段代碼

是不是看到sender,就有種很熟悉的感覺!!!先不忙著急,我們先看下事件的聲明和使用

有這樣一個應用場景,如果系統有異常,需要及時的通知管理員。那麼需要在我們的日誌記錄裡面添加通知管理員的功能,但是問題來了,該怎麼通知管理員呢?至少現在無法知道。所以我們就需要在使用到事件。

添加代碼如下,如果不知道日誌功能的可以參考【Log4Net 日誌記錄的實現】

//聲明一個通知的委托  
public delegate void NoticeEventHander(string message);  
//在委托的機制下我們建立以個通知事件  
public static event NoticeEventHander OnNotice;

調用方式

public static void Debug(string message, Action RegistedProperties)  
{  
    RegistedProperties();  
    log.Debug(message);  
    //執行通知  
    OnNotice?.Invoke($"系統異常,請及時處理,異常信息:{message}");  
}  

在引用場景的代碼,先定義一個通知管理員的方法(這裡我們直接Console.WriteLine出來)

public static void Notice(string message)  
{  
    Console.WriteLine($"通知內容為{message}");  
}

先註冊,然後觸發異常消息

//註冊方式一  
PFTLog.OnNotice += Notice;  
//註冊方式二  
//PFTLog.OnNotice += new PFTLog.NoticeEventHander(Notice);  
  
PFTLog.Debug("測試擴展欄位", () => {  
    LogicalThreadContext.Properties["LogType"] = "擴展欄位內容";  
});  

運行結果

oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP1oEH5iczqWCB3TDSviaDzoaiaLTnMr2r4HE14Acpp27eoib8v3H9htwYJOw

這裡面我只需要定義好發佈者,你可以以任何方式訂閱,是不是很非常簡單。

弄明白了上面的事件,我們在來說說.Net經常出現的object sender和EventArgs e

.Net Framework的編碼規範:

一、委托類型的名稱都應該以EventHandler結束

二、委托的原型定義:有一個void返回值,並接受兩個輸入參數:一個Object 類型,一個 EventArgs類型(或繼承自EventArgs)

三、事件的命名為 委托去掉 EventHandler之後剩餘的部分

四、繼承自EventArgs的類型應該以EventArgs結尾

現在我們以一個新書發佈的自定義事件為例

創建對應的類文件:oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP1BJWGMZSmCmyrh90B86rt7YRw6RNiasQKHlHIq9PM1K9Z9D5qP6Azn8Q

事件者發佈代碼:

public class BookInfoEventArgs : EventArgs  
{  
    public BookInfoEventArgs(string bookName)  
    {  
        BookName = bookName;  
    }  
  
    public string BookName { get; set; }  
  
}    
public class BookDealer  
{  
    //泛型委托,定義了兩個參數,一個是object sender,第二個是泛型 TEventArgs 的e  
    //簡化瞭如下的定義  
    //public delegate void NewBookInfoEventHandler(object sender, BookInfoEventArgs e);  
    //public event NewBookInfoEventHandler NewBookInfo;  
    public event EventHandler<BookInfoEventArgs> NewBookInfo;  
    public void NewBook(string bookName)  
    {  
        RaiseNewBookInfo(bookName);  
    }  
  
    public void RaiseNewBookInfo(string bookName)  
    {  
        NewBookInfo?.Invoke(this, new BookInfoEventArgs(bookName));  
    }  
}  

事件訂閱者

public class Consumer  
{  
    public Consumer(string name)  
    {  
        Name = name;  
    }  
  
    public string Name { get; set; }  
  
    public void NewBookHere(object sender, BookInfoEventArgs e)  
    {  
        Console.WriteLine($"用戶:{Name},收到書名為:{ e.BookName}");  
    }  
}  

事件訂閱和取消訂閱

var dealer = new BookDealer();  
var consumer1 = new Consumer("用戶A");  
dealer.NewBookInfo += consumer1.NewBookHere;  
dealer.NewBook("book112");  
var consumer2 = new Consumer("用戶B");  
dealer.NewBookInfo += consumer2.NewBookHere;  
dealer.NewBook("book_abc");  
  
dealer.NewBookInfo -= consumer1.NewBookHere;  
dealer.NewBook("book_all");  

運行結果

oDaAmt9QDicSHv2w5zGuUnhPR5VU27JP16I7NTMrvlQzO4E6OUiaJpKDXugetSeK6Is7pHgfiaDQtHjFHsl0aRRkw

經過這個例子,我們可以知道Object sender參數代表的是事件發佈者本身,而EventArgs e
也就是監視對象了。深入理解之後,是不是覺得也沒有想象中的那麼難了。

四、總結

這裡我們講了委托和事件,在.Net開發中使用委托和事件,可以減少依賴性和層的耦合,開發出具有更高的重用性的組件。


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

-Advertisement-
Play Games
更多相關文章
  • python的環境以及IDE都準備好之後,我們就可以開始Python之旅了。Python的第一個程式通常是列印輸出"Hello, World!",非常簡單。以下是一個示例: ```python print("Hello, World!") ``` # 運行python代碼 首先必須明白python是 ...
  • ## SQL 類 MyBatis 提供了一個 SQL 工具類,使用這個工具類,我們可以很方便在 Java 代碼動態構建 SQL 語句 ```java String newSql = new SQL() ({ SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL N ...
  • # 背景 公司最近的業務大量涉及安可項目,要求避免使用第三方組件,原有開發框架支持本地文件存儲/Minio/各類雲存儲,現在要求文件獨立存儲且文件服務需要自研,經調研評估後決定基於SpringBoot開發文件存儲服務,使用s3協議標準,這樣可以直接使用aws-sdk接入無需再開發客戶端,且安全安全性 ...
  • ![image.png](https://cdn.nlark.com/yuque/0/2023/png/2548312/1690078539162-4a2c1ab0-6ab8-4c04-b83b-b15517f0df8a.png#averageHue=%23040100&clientId=u8654 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 前文咱們曾提到過幾種啟動方式,有一種用m ...
  • [toc] # 一、爬取目標 您好,我是[@馬哥python說](https://www.zhihu.com/people/13273183132),一名10年程式猿。 本次爬取的目標是:[抖音熱榜](https://www.douyin.com/hot) ![抖音熱榜頁面](https://img ...
  • # 簡介 主流的識別庫主要有ZXing.NET和ZBar,OpenCV 4.0後加入了QR碼檢測和解碼功能。本文使用的是ZBar,同等條件下ZBar識別率更高,圖片和部分代碼參考[在C#中使用ZBar識別條形碼](https://www.cnblogs.com/w2206/p/7755656.htm ...
  • # Avalonia中用FluentAvalonia+DialogHost.Avalonia實現界面彈窗和對話框 本文是項目中關於 **`彈窗界面`** 設計的技術分享,通過 **`FluentAvalonia`+`DialogHost.Avalonia`** 開源nuget包來實現項目中需要 ** ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...