1. 程式人生 > >C#中的代理委託和event關鍵字

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;
   }
}