C#委托(delegate、Action、Func、predicate)和事件

来源:https://www.cnblogs.com/snailblog/archive/2019/09/14/11520438.html
-Advertisement-
Play Games

一、前言 剛開始工作的時候,覺得委托和事件有些神秘,而當你理解他們之後,也覺得好像沒有想象中的那麼難。在項目中運用委托和事件,你會發現他非常棒,這篇博文算是自己對委托和事件的一次梳理和總結。 二、委托 C#中的委托,相當於C++中的指針函數,但委托是面向對象的,是安全的,是一個特殊的類,當然他也是引 ...


一、前言

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

二、委托

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

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> 表示無參,返回值為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}");

運行結果

image

2.4、predicate

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

predicate<int> 表示傳入參數為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)}");

運行結果

image

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);

運行結果:

image

三、事件

事件是基於委托,為委托提供一種發佈/訂閱機制,聲明事件需要使用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"] = "擴展欄位內容";
});

運行結果

image

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

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

.Net Framework的編碼規範:

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

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

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

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

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

創建對應的類文件:image

事件者發佈代碼:

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");

運行結果

image

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

四、總結

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


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

-Advertisement-
Play Games
更多相關文章
  • [TOC] 題目 "P3958 乳酪" 思路 並查集。將乳酪的下錶面設為$0$號,輸入空洞位置時判斷該空洞如果與下錶面相切或相交就和到一個集合里,最後找到與上錶面相切或相交的空洞判斷與$0$號是否在一個集合里。 $Code$ ...
  • Eureka服務治理 下麵請聽第一個話題,母。。。咳咳,拿錯書了。 Eureka簡介 eureka是什麼呢? 簡單來說呢,當我的微服務應用多了起來,一個一個寫死再程式里是件很不優雅的事情,而且同一服務可能會多個實例存在,來對服務分流,就是負載均衡。 所以,我們需要一個位置來存放服務的訪問列表,以供消 ...
  • [TOC] 控制流程之for迴圈 基本語法 while可以迴圈一切事物 for 迴圈提供了一種手段,不依賴索引取值 for+break for+continue for+else for迴圈不被break終止就執行else後面的代碼,否則就不執行 for迴圈列印lodaing 數字類型內置方法 整型 ...
  • [TOC] 原文鏈接: "QRowTable表格控制項(五) 重寫表頭排序、支持第三次單擊恢復預設排序" 一、原生表格 開發客戶端程式的方式月來源多了,現在很流行的libcef、electron等等都可以作為快速開發客戶端軟體的方案,但是如果需要一個號的用戶體驗,還是離不開原生化的開發,雖然慢,但是性 ...
  • 簡介 簡單來說,springcloud的就是由一組springboot應用(服務)組成,相互之間通過REST等方式進行通信。 兩個springboot應用,其中一個作為服務提供者,一個作為服務消費者,我認為這就構成了一個最簡單的springcloud應用,之後其他的工具都是為這兩個應用來服務的。 我 ...
  • 源代碼:# dict1 是 字典 , 用來對應相應元素的下標,我們將文件轉成列表,對應的也就是文件的下標,通過下標來找文件元素dict1 = {'sort':0 , 'name':1 ,'age':2 ,'phone':3 ,'job':4 }#將最後需要列印的信息轉成列表的形式def p_mess ...
  • 今日所學: /* 2019.08.19開始學習,此為補檔。 */ 1.this: ①this是成員方法的一個特殊的固有的本地變數,它表達了調用這個方法的那個對象。 ②在成員方法內部直接調用自己(this)的其他方法。 2.本地(局部)變數: ①定義在方法內部的變數是本地變數。 ②本地變數的生存期和作 ...
  • 一、前言 一直想寫一篇Dpper的定製化擴展的文章,但是裡面會設計到對Lambda表達式的解析,而解析Lambda表達式,就必須要知道表達式樹的相關知識點。我希望能通過對各個模塊的知識點或者運用能夠多一點的講解,能夠幫助到園友瞭解得更多。雖然講解得不全面,如果能成為打開這塊的一把鑰匙,也是蝸牛比較欣 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...