一、前言
剛開始作業的時候,覺得委托和事件有些神秘,而當你理解他們之后,也覺得好像沒有想象中的那么難,在專案中運用委托和事件,你會發現他非常棒,這篇博文算是自己對委托和事件的一次梳理和總結,
二、委托
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()}");
運行結果:

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}");
運行結果

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)}");
運行結果

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);
運行結果:

三、事件
事件是基于委托,為委托提供一種發布/訂閱機制,宣告事件需要使用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"] = "擴展欄位內容";
});
運行結果

這里面我只需要定義好發布者,你可以以任何方式訂閱,是不是很非常簡單,
弄明白了上面的事件,我們在來說說.Net經常出現的object sender和EventArgs e
.Net Framework的編碼規范:
一、委托型別的名稱都應該以EventHandler結束
二、委托的原型定義:有一個void回傳值,并接受兩個輸入引數:一個Object 型別,一個 EventArgs型別(或繼承自EventArgs)
三、事件的命名為 委托去掉 EventHandler之后剩余的部分
四、繼承自EventArgs的型別應該以EventArgs結尾
現在我們以一個新書發布的自定義事件為例
創建對應的類檔案:
事件者發布代碼:
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");
運行結果

經過這個例子,我們可以知道Object sender引數代表的是事件發布者本身,而EventArgs e 也就是監視物件了,深入理解之后,是不是覺得也沒有想象中的那么難了,
四、總結
這里我們講了委托和事件,在.Net開發中使用委托和事件,可以減少依賴性和層的耦合,開發出具有更高的重用性的組件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/94864.html
標籤:C#
上一篇:你真的了解foreach嗎?
下一篇:C#運算式樹
