【學習資料】
《C#圖解教程》(第13~14章):https://www.cnblogs.com/moonache/p/7687551.html
電子書下載:https://pan.baidu.com/s/1mhOmBG0
- 其他
> 委托與事件詳解Part1:http://www.tracefact.net/tech/009.html
> 委托與事件詳解Part2:http://www.tracefact.net/tech/029.html
【內容】
-
- 委托(Delegate)
- 定義:類似C++的函數指針
- 委托多播
- 委托綁定函數及執行原理
- 事件(Event)
- 定義:類似 字段與屬性 的關系
- 事件訪問器(add/remove)
- 為什么使用事件?
- 委托(Delegate)
【委托Delegate】
- 定義
- 類似C++的函數指針,delegate相當於是 指向某對象的某個函數,的 引用類型變量
- 委托和類一樣,也需要 聲明、創建、賦值
- 聲明委托類型(像類一樣聲明一個委托類型) public delegate [函數返回值] 委托名稱(參數1,參數2...);
- 創建委托變量 MyDelegate opt = new MyDelegate(AddNum); (實際綁定的是:this.AddNum)
- 重新賦值 opt = MultNum; (實際綁定的是:this.MultNum)
- 執行委托:就是執行引用的 委托函數
- 委托可以:動態修改引用的函數
-
// 聲明:一個可以指向 返回類型為int,帶2個int參數的 函數 public delegate int MyDelegate(int a, int b); // 相加函數 public int AddNum(int a, int b) { return (a + b); } // 相乘函數 public int MultNum(int a, int b) { return (a * b); } void Start() { // 創建:指向不同函數的委托類型 MyDelegate opt = new MyDelegate(AddNum); Debug.Log(opt(2, 5)); // 執行委托函數: 7 // 運行時,可以改變賦值 opt = MultNum; Debug.Log(opt(2, 5)); // 執行委托函數: 10 }
- 委托多播
- 創建的委托類型變量 opt 可以存儲 函數調用列表(綁定多個委托函數)
- 通過 "+",或"+=":添加 一個函數引用
- 通過 "-",或"-=" :移除 某個函數的引用
- 執行委托:會執行所有添加綁定的 委托函數
-
public delegate void MyDelegate(int a, int b); // 輸出相加結果 public void PrintAddNum(int a, int b) { Debug.Log("PrintAddNum:" + (a + b)); } // 輸出相乘結構 public void PrintMultNum(int a, int b) { Debug.Log("PrintMultNum:" + (a * b)); } void Start() { // 創建:指向不同函數的委托類型 MyDelegate opt = new MyDelegate(PrintAddNum); // 通過+=添加委托函數 // 也可以直接相加 opt = PrintAddNum + PrintMultNum; opt += PrintMultNum; // 執行委托,會按添加的順序,分別執行PrintAddNum和PrintMultNum opt(2, 5); // 通過-=移除對某函數的引用 opt -= PrintAddNum; // 再次執行委托,只執行了PrintMultNum opt(2, 5); } // 輸出 // PrintAddNum: 7 // PrintMultNum: 10 // PrintMultNum: 10
- 委托綁定函數及執行原理
- 綁定委托函數
- 可以通過:“=”、“+=”、“-=”、“+”、“-”,來重新給委托綁定函數,或添加刪除函數
- 綁定普通函數:同時會將對象(this)引用存儲起來
- 綁定靜態函數:無需存儲對象(this)引用
-
class Person { public delegate void MyDelegate(int a, int b); public MyDelegate myDelegate; // 普通函數,綁定時需要有明確對象(this) public void Func1(int a, int b) { ... } // 靜態函數,通過類名.Func2綁定 public static void Func2(int a, int b) { ... } } void Start() { Person myPerson = new Person(); //myPerson.myDelegate += Person.Func1; // 報錯,Func1需要對象(this) myPerson.myDelegate += Person.Func2; myPerson.myDelegate += myPerson.Func1; //myPerson.myDelegate += myPerson.Func2; // 報錯,Func2是靜態函數 }
- 執行方式
- 通過"+" 或 "+=" 的順序,依次執行綁定的委托函數
- 普通函數:相當於 調用 綁定對象引用(this)的函數,包含了對象的this引用
- 靜態函數:相當於 調用 綁定的委托函數;
-
// 聲明委托類型 public delegate void MyDelegate(); class Person { public string name; public void PrintName() { Debug.Log(name); } } void Start() { // 創建2個對象 Person myPerson = new Person(); Person myPerson2 = new Person(); myPerson.name = "Alice"; myPerson2.name = "Bob"; // 創建委托變量 MyDelegate myDelegate = new MyDelegate(myPerson.PrintName); // 多播:委托函數 myDelegate += myPerson2.PrintName; // 執行委托 myDelegate(); } // 輸出 // Alice // Bob
- 綁定委托函數
【事件】
- 定義
- 事件是對委托的封裝,關系和字段與屬性(Property) 一樣
- 事件是基於委托,所以首先要聲明委托,再定義事件
-
// 聲明委托類型 public delegate void MyDelegate(int a, int b); // 定義事件 public event MyDelegate myEvent;
- 注:事件的聲明只能在類內部,不能在函數內
- 原理
- 實際上與屬性一樣,定義一個事件,會自動創建一個隱藏(private)的委托類型變量
- 並包含了add_xxx("+=")和remove_xxx("-="),用來添加/刪除 委托函數
- 事件的執行:只能在定義事件的類內部執行
-
// 聲明委托類型 public delegate void MyDelegate(int a, int b); class Person { // 定義事件 public event MyDelegate myEvent; public void FireEvent(int a, int b) { if (myEvent != null) myEvent(a, b); // 必須在定義事件的類內部執行 } } public void PrintAddNum(int a, int b) { Debug.Log("PrintAddNum:" + (a + b)); } public void PrintMultNum(int a, int b) { Debug.Log("PrintMultNum:" + (a * b)); } void Start() { Person person = new Person(); person.myEvent += PrintAddNum; person.myEvent += PrintMultNum; //person.myEvent(2, 5); // 報錯,無法在定義事件的類外部執行 person.FireEvent(2, 5); // 正確 }
- 注:事件不能在類的外部使用賦值“=”,但是在類內部可以重新賦值“=”
- 事件與委托一樣,也支持多播:可以通過“+=”、“-=” 來添加事件、刪除事件
- 事件訪問器(add / remove)
- 事件與委托的關系,與 字段與屬性的關系是一樣的,所以也有訪問器
- 關鍵字: add remove
-
public event MyDelegate myEvent { add { ... //執行 += 運算符的代碼 } remove { ... //執行 -= 運算符的代碼 } }
- 為什么使用事件?
- 對委托的封裝
- 對外只能通過事件來 添加/刪除 綁定的委托函數
- 事件只能在定義事件的 類內部執行
- 滿足觀察者模式(發布者-訂閱者)
- 當然不使用事件,也可以實現對委托的封裝
- 將委托定義為private,然后對外提供添加/刪除委托函數的方法(相當於手動實現事件中的add_xxx和remove_xxx)
-
// 聲明委托類型 public delegate void MyDelegate(int a, int b); class Person { // 定義一個私有委托變量 public MyDelegate myDelegate; // 添加 public void AddEvent(MyDelegate d) { myDelegate += d; } // 刪除 public void RemoveEvent(MyDelegate d) { myDelegate -= d; } // 執行 public void FireEvent(int a, int b) { if (myDelegate != null) myDelegate(a, b); // 必須在定義事件的類內部執行 } } public void PrintAddNum(int a, int b) { Debug.Log("PrintAddNum:" + (a + b)); } public void PrintMultNum(int a, int b) { Debug.Log("PrintMultNum:" + (a * b)); } void Start() { Person person = new Person(); person.AddEvent(PrintAddNum); // this.PrintAddNum person.RemoveEvent(PrintMultNum); // this.PrintMultNum person.FireEvent(2, 5); }
- 對委托的封裝