delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),並且它”只能持有與它的簽名相匹配的方法的引用“。它所實現的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數委託和事件在 .Net Framework中的應用非常廣泛指針不具備的優點。首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效地址或者越界地址。
實現一個delegate是很簡單的,通過以下3個步驟即可實現一個delegate:
1、聲明一個delegate對象,它應當與你想要傳遞的方法具有相同的參數和返回值類型。
2、創建delegate對象,並”將你想要傳遞的函數作爲參數傳入”。
3、在要實現異步調用的地方,通過上一步創建的對象來調用方法。
using System;
public class MyDelegateTest
{
// 步驟1,聲明delegate對象
public delegate void MyDelegate(string name);
// 這是我們欲傳遞的方法,它與MyDelegate具有相同的參數和返回值類型
public static void MyDelegateFunc(string name)
{
Console.WriteLine("Hello, ", name);
}
public static void Main()
{
// 步驟2,創建delegate對象(實例??)
MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
// 步驟3,調用delegate
md("sam1111");
}
}
輸出結果是:Hello, sam1111
瞭解了delegate,下面我們來看看,在C#中對事件是如何處理的。
C#中的事件處理實際上是一種具有特殊簽名的delegate,象下面這個樣子:
public delegate void CalculateEventHandler(object sender, MyEventArgs e);
其中的兩個參數,sender代表事件發送者,e是事件參數類。MyEventArgs類用來包含與事件相關的數據,所有的事件參數類都必須從System.EventArgs類派生。當然,如果你的事件不含參數,那麼可以直接用System.EventArgs類作爲參數。
就是這麼簡單,結合delegate的實現,我們可以將自定義事件的實現歸結爲以下幾步:
1、定義delegate對象類型,它有兩個參數,第一個參數是事件發送者對象,第二個參數是事件參數類對象。
2、定義事件參數類,此類應當從System.EventArgs類派生。如果事件不帶參數,這一步可以省略。
3、定義”事件處理方法,它應當與delegate對象具有相同的參數和返回值類型”。
4、用event關鍵字定義事件對象,它同時也是一個delegate對象。
5、用+=操作符添加事件到事件隊列中(-=操作符能夠將事件從隊列中刪除)。
6、在需要觸發事件的地方用調用delegate的方式寫事件觸發方法。一般來說,此方法應爲protected訪問限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。
7、在適當的地方調用事件觸發方法觸發事件。
public class Calculator
{
/// <summary>
/// 定義一個CalculateEventArgs
/// 用於存放事件引發時向處理程序傳遞的狀態信息
/// </summary>
public class CalculateEventArgs : EventArgs
{
public readonly int x, y;
public CalculateEventArgs(int x, int y)
{
this.x = x;
this.y = y;
}
}
//聲明事件委託
public delegate void CalculateEventHandler(object sender, CalculateEventArgs e);
//定義事件成員,提供外部綁定
public event CalculateEventHandler MyCalculate;
//提供受保護的虛方法,可以由子類覆寫來拒絕監視
public virtual void OnCalculate(CalculateEventArgs e)
{
if (MyCalculate != null)
{
MyCalculate(this, e);
}
}
//進行計算,調用該方法表示有新的計算髮生
public void Calculate(int x, int y)
{
CalculateEventArgs e = new CalculateEventArgs(x, y);
OnCalculate(e);
}
}
一件事件的完整程序就這樣定義好了,然後需要一個事件觸發程序,用來監聽事件:
/// <summary>
/// 定義事件觸發者
/// </summary>
public class CalculateManager
{
/// <summary>
/// 定義信息通知方法
/// </summary>
public void Add(object sender, Calculator.CalculateEventArgs e)
{
Console.WriteLine(e.x + "==========" + e.y);
}
public void Substract(object sender, Calculator.CalculateEventArgs e)
{
Console.WriteLine(e.x + "==========" + e.y);
}
}
之後就是實現事件的處理
/// <summary>
///
/// </summary>
public class Test_Calculator
{
public static void Main11()
{
Calculator calculator = new Calculator();
//事件觸發者
CalculateManager cm = new CalculateManager();
//事件綁定
calculator.MyCalculate += cm.Add;
calculator.Calculate(100, 200);
calculator.MyCalculate += cm.Substract;
calculator.Calculate(100, 200);
//事件註銷
calculator.MyCalculate -= cm.Add;
calculator.Calculate(100, 200);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
public class Heater
{
private int temperature;
public string type = "RealFire 001"; // 添加型號作爲演示
public string area = "China Xian"; // 添加產地作爲演示
//聲明委託
public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
public event BoiledEventHandler Boiled; //聲明事件
// 定義BoiledEventArgs類,傳遞給Observer所感興趣的信息
public class BoiledEventArgs : EventArgs
{
public readonly int temperature;
public BoiledEventArgs(int temperature)
{
this.temperature = temperature;
}
}
// 可以供繼承自 Heater 的類重寫,以便繼承類拒絕其他對象對它的監視
protected virtual void OnBoiled(BoiledEventArgs e)
{
if (Boiled != null)
{ // 如果有對象註冊
Boiled(this, e); // 調用所有註冊對象的方法
}
}
// 燒水。
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature > 95)
{
//建立BoiledEventArgs 對象。
BoiledEventArgs e = new BoiledEventArgs(temperature);
OnBoiled(e); // 調用 OnBolied方法
}
}
}
}
// 警報器
public class Alarm
{
public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
{
Heater heater = (Heater)sender; //這裏是不是很熟悉呢?
//訪問 sender 中的公共字段
Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
Console.WriteLine("Alarm: 嘀嘀嘀,水已經 {0} 度了:", e.temperature);
Console.WriteLine();
}
}
// 顯示器
public class Display
{
public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
{ //靜態方法
Heater heater = (Heater)sender;
Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
Console.WriteLine("Display:水快燒開了,當前溫度:{0}度。", e.temperature);
Console.WriteLine();
}
}
static void Main(string[] args)
{
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.Boiled += alarm.MakeAlert; //註冊方法
//heater.Boiled += (new Alarm()).MakeAlert; //給匿名對象註冊方法
//heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert); //也可以這麼註冊
heater.Boiled += Display.ShowMsg; //註冊靜態方法
heater.BoilWater(); //燒水,會自動調用註冊過對象的方法
}
}
}