六、委託和事件
1、委託
委託是一個可以對方法進行引用的類。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。
委託的類型是安全的,給定委託的執行個體可以表示任何類型的任何對象上的執行個體方法或靜態方法--只要方法的簽名匹配於委託的簽名即可。
2、匿名方法
用作委託參數的一個代碼塊。
匿名方法中不能使用跳躍陳述式跳到匿名方法外部,也不能從外部跳到匿名方法內部。
匿名方法內部不能訪問不安全的代碼。也不能訪問在匿名方法外部使用的ref和out參數,但可以使用匿名方法外部定義的其他變數。
多播委託:可以按順序連續調用多個方法。為此,委託的簽名就必須返回void。
3、事件
事件是對象發送的訊息,以發訊號通知操作的發生。可以理解為一個或多個委託
使用委託的優點,委託和事件的區別和聯絡:
C#中的委託類似於C或C++中的函數指標。使用委託使程式員可以將方法引用封裝在委派物件內。然後可以將該委派物件傳遞給可調用所引用方法的代碼,而不必在編譯時間知道將調用哪個方法。與C或C++中的函數指標不同,委託是物件導向,而且是型別安全的。
C#中的“事件”是當對象發生某些事情時,類向該類的客戶提供通知的一種方法。事件最常見的用途是用於圖形化使用者介面;通常,表示介面中的控制項的類具有一些事件,當使用者對控制項進行某些操作(如單擊某個按鈕)時,將通知這些事件。
使用委託來聲明事件。委派物件封裝一個方法,以便可以匿名調用該方法。事件是類允許客戶為其提供方法(事件發生時應調用這些方法)的委託的一種方法。事件發生時,將調用其客戶提供給它的委託。
註明:委託是對方法的封裝 在不確定要調用什麼方法時候而又不能用抽象或者多態實現的時候用委託。
比如一個button,當點擊肯定要觸發事件,做一些處理,如果你是這個控制項的開發人員,你怎麼知道當點擊是要處理什嗎?你這個button會被什麼容器所包含?所以你必須公布一個event出去,用控制項的人具體去實現其功能。
參考:
1、如下情況宜使用委託:
只調用單個方法時.
當一個類需要方法說明的多重執行時.
期望使用靜態方法執行規範時.
期望得到一個類似事件的模式時.
調用者無需知道無需擷取定義方法的對象時
只想給少數既定組件分發執行規範時.
想要簡單的組成結構時.
2、如下情況宜使用介面:
當規範定義了一組需要調用的相關方法時.
一個類僅代表性地執行一次規範時.
介面的調用者想映射介面類型以擷取其他類或介面時
3、對函數指標以.net的方式進行的封裝,其實就是一個class。
應用最多的就是事件,在事件的情況下委託就變成了對回呼函數指標的封裝。
看一個橫刀天笑在寫觀察者模式的一個控制台程式例子:程式接收一個0到100之間整型的輸入,程式接收到輸入後開始一個從0到100的迴圈,當迴圈到你輸入的數位時候輸出此數,並快顯視窗顯示此數。
using System;
using System.Windows.Forms;
namespace Study
//定義一個委託,這裡定義了觀察者方法的簽名,就是一個協議吧
public delegate void NumberEventHandler(object sender, NumberEvantArgs e);
//要傳遞哪些參數到觀察者?在這裡定義,注意,要繼承自EventArgs
public class NumberEvantArgs : EventArgs
{
public NumberEvantArgs(int number)
{
_number = number;
}
private int _number;
public int Number
{
get { return _number; }
set { _number = value; }
}
}
//觀察者模式中的主題
public class Subject
{
//定義一個事件,就是委託的執行個體
public event NumberEventHandler NumberReached;
public void DoWithLoop(int number)
{
for (int i = 0; i <= 100; i++)
{
//觸發事件的條件到了
if (i == number)
{
NumberEvantArgs e = new NumberEvantArgs(i);
OnNumberReached(e);
}
}
}
//注意,這個方法定義為保護的,虛擬,代表子類還可以進行覆蓋,改變觸發事件的行為,甚至可以不觸發事件
protected virtual void OnNumberReached(NumberEvantArgs e)
{
//判斷事件是否為null,也就是是否綁定了方法
if (NumberReached != null)
NumberReached(this, e);
}
}
public class MainProgram
{
public static void Main()
{
Console.WriteLine("Please intput a 0-100 number:");
int input=Int32.Parse(Console.ReadLine());
if (input < 0 || input > 100)
{ Console.WriteLine("Error"); }
Subject s = new Subject();
//給事件Binder 方法,靜態
s.NumberReached += new NumberEventHandler(msgbox_NumberReached);
MainProgram mp = new MainProgram();
//給事件Binder 方法,執行個體方法
s.NumberReached += new NumberEventHandler(mp.console_NumberReached);
s.DoWithLoop(input);
Console.ReadLine();
}
void console_NumberReached(object sender, NumberEvantArgs e)
{
Console.WriteLine(e.Number.ToString());
}
static void msgbox_NumberReached(object sender, NumberEvantArgs e)
{
MessageBox.Show(e.Number.ToString());
}
}
}