##Delegate委托
delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),并且它"隻能持有與它的簽名相比對的方法的引用"。
它所實作的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數委托和事件在 .Net Framework中的應用非常廣泛指針不具備的優點。首先,函數指針隻能指向靜态函數,而delegate既可以引用靜态函數,又可以引用非靜态成員函數。在引用非靜态成員函數時,delegate不但儲存了對此函數入口指針的引用,而且還儲存了調用此函數的類執行個體的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效位址或者越界位址。
##結合delegate的實作,将自定義事件的實作歸結為以下幾步:
1.定義delegate對象類型,它有兩個參數,第一個參數是事件發送者對象,第二個參數是事件參數類對象。它應當與你想要傳遞的方法具有相同的參數和傳回值類型。
2.定義事件參數類,此類應當從System.EventArgs類派生。如果事件不帶參數,這一步可以省略。
3.定義"事件處理方法,它應當與delegate對象具有相同的參數和傳回值類型"。
4.用event關鍵字定義事件對象,它同時也是一個delegate對象。
5.用+=操作符添加事件到事件隊列中(-=操作符能夠将事件從隊列中删除)。
6.在需要觸發事件的地方用調用delegate的方式寫事件觸發方法。一般來說,此方法應為protected通路限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。
7. 在适當的地方調用事件觸發方法觸發事件。
####舉例說明:
public class EventTest
{
// 步驟1,定義delegate對象,聲明事件的委托
public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步驟2(定義事件參數類)省略
public class MyEventCls
{
// 步驟3,定義事件處理方法,它與delegate對象具有相同的參數和傳回值類型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
// 步驟4,用event關鍵字定義事件對象(聲明事件)
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
// 步驟5,用+=操作符将事件添加到隊列中(用-=操作符移除隊列的事件)
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
// 步驟6,以調用delegate的方式寫事件觸發函數
protected void OnMyEvent(System.EventArgs e)
{
if(myevent != null)
myevent(this, e);
}
public void RaiseEvent()
{
EventArgs e = new EventArgs();
// 步驟7,觸發事件
OnMyEvent(e);
}
public static void Main()
{
EventTest et = new EventTest();
Console.Write("Please input ''a'':");
string s = Console.ReadLine();
if(s == "a")
{
et.RaiseEvent();
}
else
{
Console.WriteLine("Error");
}
}
}
##總結:
1.委托是一個類,它定義了方法的類型,使得可以将方法當作另一個方法的參數來進行傳遞,這種将方法動态地賦給參數的做法,可以避免在程式中大量使用If-Else(Switch)語句,同時使得程式具有更好的可擴充性。
2.使用委托可以将多個方法綁定到同一個委托變量,當調用此變量時(這裡用“調用”這個詞,是因為此變量代表一個方法),可以依次調用所有綁定的方法。
3.在使用委托的時候,你可以像對待一個類一樣對待它。即先聲明,再執行個體化。隻是有點不同,類在執行個體化之後叫對象或執行個體,但委托在執行個體化後仍叫委托。
4.事件應該由事件釋出者觸發,而不應該由用戶端(客戶程式)來觸發。
##最後以《鴻門宴》中的一個虛拟情節來體會委托的用法:
鴻門宴之前,項羽和手下的範增和項莊商量好了,等他和劉邦喝酒的時候,項羽左手舉起酒杯,範增就出兵刺殺劉邦;項羽右手舉起酒杯,項莊就出兵刺殺劉邦;如果項羽摔碎了酒杯,範增和項莊就一起出兵刺殺劉邦。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 委托
{
class 鴻門宴
{
/// <summary>
/// 定義項羽舉杯的委托
/// </summary>
/// <param name="hand">手:左、右</param>
public delegate void RaiseEventHandler(string hand);
/// <summary>
/// 定義項羽摔碎酒杯的委托
/// </summary>
public delegate void FallEventHandler();
/// <summary>
/// 定義項羽
/// </summary>
public class XiangYu
{
/// <summary>
/// 項羽舉杯事件
/// </summary>
public event RaiseEventHandler RaiseEvent;
/// <summary>
/// 項羽摔杯事件
/// </summary>
public event FallEventHandler FallEvent;
/// <summary>
/// 定義項羽舉杯事件的觸發函數
/// </summary>
/// <param name="hand">手:左、右</param>
public void Raise(string hand)
{
Console.WriteLine("項羽{0}手舉杯", hand);
// 調用舉杯事件,傳入左或右手作為參數
if (RaiseEvent != null)
{
RaiseEvent(hand);
}
}
/// <summary>
/// 定義項羽摔碎酒杯事件的觸發函數
/// </summary>
public void Fall()
{
Console.WriteLine("項羽摔碎酒杯");
// 調用摔杯事件
if (FallEvent != null)
{
FallEvent();
}
}
}
/// <summary>
/// 定義項羽部下範增
/// </summary>
public class FanZeng
{
XiangYu xiangyu;
public FanZeng(XiangYu xiangyu)
{
this.xiangyu = xiangyu;
// 訂閱項羽的舉杯事件
xiangyu.RaiseEvent += new RaiseEventHandler(xiangyu_RaiseEvent);
// 訂閱項羽的摔杯事件
xiangyu.FallEvent += new FallEventHandler(xiangyu_FallEvent);
}
/// <summary>
/// 範增對項羽舉杯事件的響應
/// </summary>
/// <param name="hand">若項羽左手舉杯,則範增發起攻擊</param>
void xiangyu_RaiseEvent(string hand)
{
if (hand.Equals("左"))
{
Attack();
}
}
/// <summary>
/// 範增對項羽摔碎酒杯事件的響應
/// </summary>
void xiangyu_FallEvent()
{
Attack();
}
/// <summary>
/// 定義攻擊的函數
/// </summary>
public void Attack()
{
Console.WriteLine("範增發起攻擊,大喊:劉邦拿命來!");
}
}
/// <summary>
/// 部下項莊
/// </summary>
public class XiangZhuang
{
XiangYu xiangyu;
public XiangZhuang(XiangYu xiangyu)
{
this.xiangyu = xiangyu;
// 訂閱舉杯事件
xiangyu.RaiseEvent += new RaiseEventHandler(xiangyu_RaiseEvent);
// 訂閱摔杯事件
xiangyu.FallEvent += new FallEventHandler(xiangyu_FallEvent);
}
/// <summary>
/// 項莊對項羽舉杯事件的響應
/// </summary>
/// <param name="hand">若項羽右手舉杯,則攻擊</param>
void xiangyu_RaiseEvent(string hand)
{
if (hand.Equals("右"))
{
Attack();
}
}
/// <summary>
/// 項莊對項羽摔碎酒杯事件的響應
/// </summary>
void xiangyu_FallEvent()
{
Attack();
}
/// <summary>
/// 定義攻擊的函數
/// </summary>
public void Attack()
{
Console.WriteLine("項莊發起攻擊,一劍刺向劉邦!");
}
}
//主函數
static void Main(string[] args)
{
XiangYu xiangyu = new XiangYu(); // 執行個體化項羽
FanZeng fanzeng = new FanZeng(xiangyu); // 執行個體化部下範增
XiangZhuang xiangzhuang = new XiangZhuang(xiangyu); // 執行個體化部下項莊
// 宴會上項羽左手舉酒杯
xiangyu.Raise("左");
// 宴會上項羽右手舉酒杯
xiangyu.Raise("右");
// 宴會上項羽摔碎了酒杯
xiangyu.Fall();
Console.ReadLine();
// 由于範增和項莊都訂閱了項羽的事件,是以無需任何代碼,範增和項莊均會按照約定進行操作。
}
}
}