委托也可以使用+=/-=來實現“發布/訂閱”模式,示例代碼如下:
~~~text
//定義一個委托
public delegate void MyDelegate1(int x);
public static void Method1(int a)
{
Console.WriteLine($"a={a}");
}
public static void Method2(int b)
{
Console.WriteLine($"b={b}");
}
static void Main(string[] args)
{
MyDelegate1 md = null;
md += Method1;
md += Method2;
md(35);
Console.ReadLine();
}
~~~
以上程序輸出如下:
*a=35*
*b=35*
但是委托有一個弊端,它可以使用“=”將所有已經訂閱的取消,只保留=后的這一個訂閱。
為了解決這個弊端,事件event應運而生。
### 2 事件-概念的引出
事件event是一種特殊的委托,它只能+=,-=,不能直接用=。event在定義類中(發布者)是可以直接=的,但是在其他類中(訂閱者)就只能+= -=了,**也就是說發布者發布一個事件后,訂閱者針對他只能進行自身的訂閱和取消。**下面是定義一個事件的代碼:
~~~text
//定義一個委托
public delegate void MyDelegate1(int x);
//定義一個事件
public event MyDelegate1 emd;
~~~
經過長久的經驗積累后,人們發現,絕大多數事件的定義,是用public delegate void XXX(object sender, EventArgs e);這樣一個委托原型進行定義的,是一件重復性的工作,于是,EventHandler應運而生。它的出現就是為了避免這種重復性工作,并建議盡量使用該類型作為事件的原型。
~~~text
//@sender: 引發事件的對象
//@e: 傳遞的參數
public delegate void EventHandler(object sender, EventArgs e);
//使用
public event EventHandler emd;
~~~
下面給出一個使用事件的具體示例:
~~~text
public class Demo
{
public event EventHandler emd;
public void RaiseEvent()
{
emd(this, EventArgs.Empty);
}
}
static void Main(string[] args)
{
var instance = new Demo();
instance.emd += (sender, arg) =>
{
Console.WriteLine("執行事件1!");
};
instance.emd += (sender, arg) =>
{
Console.WriteLine("執行事件2!");
};
instance.RaiseEvent();
Console.ReadLine();
}
~~~
這里我們先定義一個Demo類,其內部有個事件是emd,我們給他開放了一個接口RaiseEvent,如果誰敢調用它,那么,它就觸發報警事件emd。
這里模擬了2個訂閱者,分別處理報警事件emd。程序執行結果如下:執行事件1!執行事件2!同時,我們也可以看出:**事件是按照+=的訂閱先后順序執行的。**
### 3 事件-關于異常
現在,我們在第一個訂閱者中加入異常,如下:
~~~text
instance.emd += (sender, arg) =>
{
Console.WriteLine("執行事件1!");
throw new Exception("執行事件1,錯誤");
};
~~~
執行后發現,第1個訂閱者事件觸發拋出異常后,第2個訂閱者的事件沒有執行。
**可見,如果你想讓所有訂閱者都執行處理的話,那每個訂閱者必須在訂閱程序內自己處理好異常,不能拋出來!**
### 4 事件-關于異步
如果事件的訂閱者中有一個是“異步”處理,又會是什么情況?
下面我們把第1個訂閱者改為異步處理,代碼如下:
~~~text
instance.emd += async (sender, arg) =>
{
Console.WriteLine("執行事件1!");
await Task.Delay(1000);
Console.WriteLine("執行事件1!完畢");
};
~~~
執行后輸出如下:
*執行事件1!*
*執行事件2!*
*執行事件1!完畢*
可見,異步的事件處理沒有阻塞進程,很好的起到了異步方法的作用。
### 5 委托-Func與Action
本文最開始探討委托,然后直接順到了事件的相關話題上。其實,關于委托還有一個重點話題漏掉了,那就是Func與Action。
在委托delegate出現了很久以后,微軟的.NET設計者們終于領悟到,其實所有的委托定義都可以歸納并簡化成只用Func與Action這兩個語法糖來表示。其中,Func代表有返回值的委托,Action代表無返回值的委托。有了它們兩,我們以后就不再需要用關鍵字delegate來定義委托了。
同時,若再用lambda表達式取代被委托指向的具體方法,**則整個委托的“定義+賦值”兩步將大大簡化**(lambda表達式本來也是方法定義的一種簡化形式)。
下面,把最開始委托章節中關于加減法的程序代碼,用Func與lambda表達式進行簡化改造,改造后的代碼如下:
~~~text
//示例:將委托/方法當參數傳遞
public static int Test(Func<int, int, int> MD)
{
return MD(10, 20);
}
static void Main(string[] args)
{
int a, b, x, y;
Func<int, int, int> md;
//將委托指向加法,并進行相關操作
md = (t, v) => t + v;
a = md(1, 2);
b = Test(md);
//再將委托指向減法,并進行相關操作
md = (t, v) => t - v;
x = md(7, 2);
y = Test(md);
Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
Console.ReadLine();
}
~~~
- Visual Studio 2022安裝到非C盤
- .net平臺區別
- 常用單詞
- 關鍵字
- 操作符(運算符)
- 標識符(命名規范)
- 開始
- 變量
- 常量
- 數據類型
- 值類型
- 變量數據類型
- 枚舉類型enum(常量集合)
- 結構類型struct(結構體)
- 元組類型
- 可null類型(T?)
- 引用類型
- 數組(array)
- 集合(List)
- 內置引用類型
- object
- string
- Dynamic(動態類型)
- delegate委托(代理)類型
- 自定義引用類型
- 接口(interface)
- 類class
- record(定義一個引用類型)
- 指針類型(僅用于非安全代碼)
- get和set訪問器
- delegate委托
- delegate實現發布訂閱與事件
- 類型轉換
- 合并操作符??
- 類相關
- Partial 部分類
- 類定義以及訪問修飾符(封裝)
- abstract抽象類與sealed密封類
- virtual虛方法
- 接口interface
- C# 預處理器指令
- C#技術棧
- 判斷(流程控制)與三元運算
- if
- switch
- 三元運算
- 循環
- while 循環
- for循環
- foreach循環
- do...while 循環
- 文件操作
- 其他
- 多開