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

1、前言

剛開始工做的時候,以爲委託和事件有些神祕,而當你理解他們以後,也以爲好像沒有想象中的那麼難。在項目中運用委託和事件,你會發現他很是棒,這篇博文算是本身對委託和事件的一次梳理和總結。html

2、委託

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

2.一、delegate

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

格式以下所示:post

private delegate string GetAsString();

委託是一個類,因此他的實例化跟類的實例化同樣,只是他老是接受一個將委託方法做爲參數的構造函數。調用委託方法就有兩種方式,以下所示:測試

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

運行結果:this

image

2.二、Action

Action是無返回值的泛型委託,能夠接受0個至16個傳入參數編碼

Action 表示無參,無返回值的委託spa

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.三、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.四、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.五、多播委託

前面的只包含了一個方法的調用,委託能夠包含多個方法,這種委託就叫作多播委託。多播委託利用「+=」和「-+」兩種運算符進行添加和刪除委託。

先定義兩個方法

複製代碼
複製代碼
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

3、事件

事件是基於委託,爲委託提供一種發佈/訂閱機制,聲明事件須要使用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的編碼規範:

1、委託類型的名稱都應該以EventHandler結束

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

3、事件的命名爲 委託去掉 EventHandler以後剩餘的部分

4、繼承自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 也就是監視對象了。深刻理解以後,是否是以爲也沒有想象中的那麼難了。

4、總結

這裏咱們講了委託和事件,在.Net開發中使用委託和事件,能夠減小依賴性和層的耦合,開發出具備更高的重用性的組件。

相關文章
相關標籤/搜索