C#中的代理委託和event關鍵字
也叫作委託。事實上,代理就是用於定義指向方法的引用。
比如你在你眼前的程式中要呼叫另一部分程式的內容(方法或屬性),但是,你不能保證函式名或者屬性名不發生變化,或者根本程式不可見(不是public或者是DLL的程式)。那麼就使用代理。
定義如下
public delegate UInt32[] getDownLoadParam();
public delegate void messgeFromDataLayer(Int32 info, Int32 info1, Int32 info2);
getDownLoadParam m_getDownLoadParam;
messgeFromDataLayer m_messgeFromDataLayer;
在模組初始化時為他們賦值。
m_getDownLoadParam = GetDownLoadParam;
m_messgeFromDataLayer = MessgeFromDataLayer;
函式名可以直接轉化為代理型別的物件。這樣,你就可以向普通函式一樣呼叫他們了。
m_messgeFromDataLayer(1, 0, 0);
一個完整的使用如下
using System;
namespace sandbox
{
class DelegateTest
{
public delegate void CompareDelegate(int a, int b);
// 欲傳遞的方法,它與CompareDelegate具有相同的引數和返回值型別
public static void Compare(int a, int b)
{
Console.WriteLine((a > b).ToString());
}
public static void Main()
{
// 建立delegate物件
CompareDelegate cd = new CompareDelegate(DelegateTest.Compare);
// 呼叫delegate
cd(1, 2);
}
}
除了代理外,C#還有一個常見的語法糖。event關鍵字。上面代理可以讓程式動態呼叫函式指標處理。但是,如果我們希望的處理函式不止一個,特別是對於介面上。所以,我們引入了event關鍵字。可以向中間新增若干個代理的例項,將依次呼叫。
event的宣告是event+事件支援的代理名+event名。直接用event名就可以出發事件。一個event可以新增若干個事件處理的代理例項,但他們必須都是一個代理型別的。一個代理型別可以由多個代理的例項。
using System;
namespace sandbox
{
class DelegateTest
{
public static void Main()
{
//建立事件源,並將事件處理程式進行註冊
EventSource src = new EventSource();
src.OnClick += new EventSource.ClickHandler(MyFire1);
src.OnClick += new EventSource.ClickHandler(MyFire2);
src.Click();
System.Console.ReadLine();
}
public static void MyFire1(object sender, ButtonClickArgs e)
{
Console.WriteLine(e.Clicker+" hava been shot in 1");
}
public static void MyFire2(object sender, ButtonClickArgs e)
{
Console.WriteLine(e.Clicker+" hava been shot in 2");
}
}
class EventSource
{
public delegate void ClickHandler(object sender, ButtonClickArgs e);
public event ClickHandler OnClick;
public void Click()
{
OnClick(this, new ButtonClickArgs() { Clicker = "Signal" });
}
}
public class ButtonClickArgs : EventArgs
{
public string Clicker;
}
}
這種delegate和event共同使用的情景常用語介面的設計
如果你不使用代理,而執意要用public,那麼在訪問form的子類的物件的值型別成員時,編譯器會給出CS1690警告:由於xxx是引用封送類的欄位,訪問上面的成員可能導致執行時異常。
這是因為form類是MarshalByRefObject的子類,該類旨在設計支援不同程式域的物件進行通訊。一般來說,不同程式域的通訊有兩種方式,第一種就是直接傳送物件的副本,另一種是使用代理。MarshalByRefObject使用的的是後一種。當有人要遠端訪問他的物件是,第一次他會向對方傳遞代理,然後如果對方呼叫該代理,就由己方的物件負責執行。
所以,如果你訪問form類的物件A所包含的物件B的方法時,就將程式置於依賴遠端物件執行的程式碼的風險之下,所以編譯器會發送警告。解決的方法就是把物件B先複製到本地就可以了。
using System;
class WarningCS1690: MarshalByRefObject
{
int i = 5;
public static void Main()
{
WarningCS1690 e = new WarningCS1690();
e.i.ToString(); // CS1690
// OK
int i = e.i;//c#是system.int32的別名而已
i.ToString();
e.i = i;
}
}