---
tags: C#進階VIP班
---
# 玩轉C#之【委派和事件】
## 委派的宣告、實體化和使用
宣告一個委派:
在一個沒有方法體的方法前面加上delegate
```csharp=
public delegate void NoReturnNoPara<T>(T t);
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x,int y);
public delegate int WithReturnNoPara();
public delegate string WithReturnWithPara(out int x,ref int y);
```
透過IL反編譯工具,我們可以觀察到實際上
* 宣告委託實際上是屬於一種class
* 他繼承父類MulticastDelegate
* 可以看到.ctor=>建構子
* 裡面有Invoke、BeginInvoke、EndInvoke,這些方法
![IL反編譯](https://i.imgur.com/caFb9nr.png)
總結:
委託是一個類,繼承自System.MulticastDelgate,裡面內置了幾個方法
一般使用類別的方式:
```csharp=
Student student =new Student()
{
Id = 123,
Name ="上課不乖",
Age = 32,
ClassId =1
};
sudent.Study();
```
使用一個委託:
當呼叫`method.Invoke()`的時候,實際上就只是執行一次`DoNothing()`
```csharp=
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//實體化一個委託
method.Invoke();//使用他的方法
method();//也可以省略Invoke的寫法
```
```csharp=
private void DoNothing()
{
Console.log("Test");
}
```
從目前的實做中,委託的價值看起來是
把方法包裝成變量,等到invoke的時候自動執行方法
範例題目:
```csharp=
#region 資料準備
/// <summary>
/// 資料準備
/// </summary>
/// <returns></returns>
private List<Student> GetStudentList()
{
#region 初始化資料
List<Student> studentList = new List<Student>()
{
new Student()
{
Id=1,
Name="老K",
ClassId=2,
Age=35
},
new Student()
{
Id=1,
Name="hao",
ClassId=2,
Age=23
}
#endregion
return studentList;
}
#endregion
```
當有一些需求是:
* 希望找到年齡大於25學生
* 希望找到名字長度大於2的學生
* 希望找出名字長度大於2且年齡大於25班級Id是2的學生
```csharp=
List<Student> studentList = this.GetStudentList();
//找出年齡大於25學生
List<Student> result = new List<Student>();//准备容器
foreach (Student student in studentList)//遍历数据源
{
if (student.Age > 25)//判断条件
{
result.Add(student);//满足条件的放入容器
}
}
Console.WriteLine($"结果一共有{result.Count()}个");
//找到名字長度大於2的學生
List<Student> result = new List<Student>();
foreach (Student student in studentList)
{
if (student.Name.Length > 2)
{
result.Add(student);
}
}
//找出名字長度大於2且年齡大於25班級Id是2的學生
List<Student> result = new List<Student>();
foreach (Student student in studentList)
{
if (student.Name.Length > 2 && student.Age > 25 && student.ClassId == 2)
{
result.Add(student);
}
}
Console.WriteLine($"结果一共有{result.Count()}个");
```
我們會發現這三個都在做重複的邏輯
1. 準備容器
2. 尋遍資料
3. 判斷條件
4. 滿足條件的塞入容器
我們來優化一下程式碼:
* 最簡單的方法 type:不同的值 不同的判断 如果有新的需求又需要再多一種
* 传个参数--判断参数--执行对应的行为
* 那我能不能直接判斷條件呢?邏輯就是方法,等于说傳遞方法近來
```csharp=
private List<Student> GetList(List<Student> source, int type)
{
List<Student> result = new List<Student>();
foreach (Student student in source)
{
if (type == 1)
{
if (student.Age > 25)//判断条件
{
result.Add(student);//满足条件的放入容器
}
}
else if (type == 2)
{
if (student.Name.Length > 2)
{
result.Add(student);
}
}
else if (type == 3)
{
if (student.Name.Length > 2 && student.Age > 25 && student.ClassId == 2)
{
result.Add(student);
}
}
}
return result;
}
```
第二種方式:
* 把判斷邏輯傳遞近來,透過委託傳遞近來
* 委託解偶=>可以減少重複程式碼
```csharp=
public delegate bool ThanDelegate(Student student);
private bool than(Student student)
{
return student.Age > 25;
}
ThanDelegate method = new ThanDelegate(than);
List<Student> result = this.GetListDelegate(studentList, method);
private List<Student> GetListDelegate(List<Student> source, ThanDelegate method)
{
List<Student> result = new List<Student>();//準備容器
foreach (Student student in source)
{
if (method.Invoke(student))
{
result.Add(student);
}
}
return result;
}
```
工作中的應用:
```csharp=
public static void SafeInvoke(Action act)
{
try
{
avt.Invoke();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
```
## 泛型委派--Func Action
.Net Framework 3.0 Linq/task
```csharp=
public class ActionFunc
{
public void Show()
{
//Action 0~16個參數的 沒有返回值 泛型 委派
Action action =()=>{};
Action<int> action2 = i=>Console.WriteLine(i);
//Func 0~16個參數的 有返回值 泛型 委派
Func<int> func1 = () =>1;
Func<int,string> func2 = i=>i.ToString();
}
}
```
為什麼要 Func Action
很多委派長的一樣 但是不能通用 會導致不同框架中 定義各種各樣的相同委派 就是浪費
所以為了統一 就全部使用標準的Action Func
```csharp=
private void DoNothing(Action act)
{
act.Invoke();
}
```
## 委派的意義:解偶
## 委派的意義:非同步多執行緒
```csharp=
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
int iResult = method.Invoke();
iResult = method();
var result = method.BeginInvoke(null,null);//非同步
method.EndInvoke(result);
```
```csharp=
private int GetSomething()
{
return 1;
}
```
## 委派的意義:Multicast Delegates(合併委派)
價值:可以在一個變量保存多個方法,可以增減,invoke的時候可以按順序執行
* +=為委派增加實體按順序增加方法,形成方法鏈,Invoke時,按順序依次執行
* -=為委派移除方法,從方法鏈的尾部開始匹配,遇到第一個完全吻合的,移除且只移除一個,沒有也不會異常
```csharp=
NoReturnNoPara method = new NoreturnNoPara(this.DoNothing);
method += new NoreturnNoPara(this.DoNothing);
method += new NoReturnNoPara(DoNothingStatic);
method += new NoReturnNoPara(Student.StudenAdvanced);
method += new NoReturnNoPara(new Student().Study);
method.Invoke();
method -= new NoReturnNoPara(DoNothingStatic);
method -= new NoReturnNoPara(Student.StudenAdvanced);
method -= new NoReturnNoPara(new Student().Study);
method.Invoke();
```
疑!!我們會發現,這裡的方法怎麼會沒有被移除呢?
* 因為不是同一個實體,所以是不同的方法
![移除方法](https://i.imgur.com/TrwzNYS.png)
:notebook: 小提示:
合併委派是不能使用非同步的,會報錯誤
```csharp=
method.BeginInvoke(null,null);
```
找出合併委派中裡面的所有方法
```csharp=
foreach(var item in method.GetInvocationList())
{
item.Invoke();
}
```
如果合併委派帶返回值,結果以最後的為準
```csharp=
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
method += new WithReturnNoPara(this.GetSomething2);
method += new WithReturnNoPara(this.GetSomething3);
int iResult = method.Invoke();//結果會是GetSomething3的數值
```
## 事件
事件:是帶event關鍵字的委派的實體,可以限制變數被外部直接使用/直接賦予值(安全保障)
特點:
* 不能直接Invoke()
* 不能 賦予 null => = null
委派和事件的區別和關係?
* 委派是一個類型
* 事件是委派類型的一個實例
事件:可以把一堆可變的動作/行為封裝出去,交給第三方指定
預定義一樣,程式設計的時候,我們可以把程式分成兩部分
一部分是固定的,直接寫死,還有不固定的地方,通過一個事件去開放接口,外部可以隨意地擴展動作
框架:完成固定/通用部分,把可變部分留出擴展點,支持自定義
觀察者模式:
![](https://rjgeek.github.io/images/2016/12/Observer_1.png?t=1%3E)
## 小作業: SQL CRUD 試試使用委派