奧東......C# 委託 事件

委託是一個類,它定義了方法的類型,使得能夠將方法看成另外一個方法的參數來進行傳遞,這種將方法動態地賦給參數的作法,能夠避免在程序中大量使用If-Else(Switch)語句,同時使得程序具備更好的可擴展性。小程序

委託不一樣於string的一個特性:能夠將多個方法賦給同一個委託,或者叫將多個方法綁定到同一個委託,當調用這個委託的時候,將依次調用其所綁定的方法this

使用委託能夠將多個方法綁定到同一個委託變量,當調用此變量時(這裏用「調用」這個詞,是由於此變量表明一個方法),能夠依次調用全部綁定的方法spa

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateDemo
{
    class DelegateDemoOne
    {
        public delegate void GreetingDelegate(string name);
        public static void GreetPeople(string name, GreetingDelegate MakeGreeting)
        {
            MakeGreeting(name);
        }
        public static void EnglishGreeting(string name)
        {
            Console.WriteLine("Morning, " + name);
        }
        public static void ChineseGreeting(string name)
        {
            Console.WriteLine("早上好, " + name);
        }
        static void Main(string[] args)
        {
            //第一種方式
            //GreetPeople("English man", EnglishGreeting);
            //GreetPeople("Chinese man", ChineseGreeting);
            //第二種方式
            //GreetingDelegate delegate1, delegate2;
            //delegate1 = EnglishGreeting;
            //delegate2 = ChineseGreeting;
            //GreetPeople("English", delegate1);
            //GreetPeople("Chinese", delegate2);
            //第三種方式
            //注意這裏,第一次用的「=」,是賦值的語法;第二次,用的是「+=」,是綁定的語法。若是第一次就使用「+=」,將出現「使用了未賦值的局部變量」的編譯錯誤
            //GreetingDelegate delegate1;
            //delegate1 =  EnglishGreeting; // 先給委託類型的變量賦值
            //delegate1 += ChineseGreeting;// 給此委託變量再綁定一個方法

            // 將前後調用 EnglishGreeting 與 ChineseGreeting 方法
            //GreetPeople("English", delegate1);
            GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
            delegate1 += ChineseGreeting;   // 給此委託變量再綁定一個方法  也進行結束綁定
            GreetPeople("new實例化 ", delegate1);

            Console.ReadKey();
        }
    }
}

面向對象化設計設計

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleDemo
{
    public delegate void GreetingDelegate(string name);
    
    //新建的GreetingManager類
    public class GreetingManager
    {
        //在GreetingManager類的內部聲明delegate1變量
        public GreetingDelegate delegate1;
        //爲了解決那種本身調用本身還傳遞本身的寫法
        //public void GreetPeople(string name, GreetingDelegate MakeGreeting)
        //{
        //    MakeGreeting(name);
        //}
        //方法改寫 傳遞過來的是委託的名稱
        public void GreetPeople(string name)
        {
            if (delegate1 != null)
            {     //若是有方法註冊委託變量
                delegate1(name);//經過委託調用方法
            }
        }
    }
    class ProgramTwo
    {
        private static void EnglishGreeting(string name)
        {
            Console.WriteLine("Morning, " + name);
        }

        private static void ChineseGreeting(string name)
        {
            Console.WriteLine("早上好, " + name);
        }
        static void Main(string[] args)
        {
            GreetingManager gm = new GreetingManager();
            //GreetingDelegate delegate1;
            //gm.delegate1 = EnglishGreeting;
            //gm.delegate1 += ChineseGreeting;
            //這條語句很奇怪,在調用gm.GreetPeople方法的時候,再次傳遞了gm的delegate1字段
            //gm.GreetPeople("Jimmy Zhang", gm.delegate1);
         
            //gm.GreetPeople("Jimmy Zhang", EnglishGreeting);
            //gm.GreetPeople("張子陽", ChineseGreeting);
            //GreetPeople方法改寫以後的調用

            gm.delegate1 = EnglishGreeting;
            gm.delegate1 += ChineseGreeting;
            gm.GreetPeople("Jimmy Zhang");      //注意,此次不須要再傳遞 delegate1變量

            Console.ReadKey();
        }

    }
    
}

 

委託爲何不能聲明爲private:code

        由於聲明委託的目的就是爲了把它暴露在類的客戶端進行方法的註冊,你把它聲明爲private了,客戶端對它根本就不可見,那它還有什麼用?對象

聲明爲public類型的好處:blog

        在客戶端能夠對它進行隨意的賦值等操做,嚴重破壞對象的封裝性事件

若是是String類型看作是委託你怎麼辦:rem

         答案是使用屬性對字段進行封裝。字符串

使用委託的時候第一個方法註冊用「=」,是賦值語法,由於要進行實例化,第二個方法註冊則用的是「+=」的終極體驗:

      Event,它封裝了委託類型的變量,使得:在類的內部,無論你聲明它是public仍是protected,它老是private的。

      在類的外部,註冊「+=」和註銷「-=」的訪問限定符與你在聲明事件時使用的訪問符相同

在GreetingManager類中添加以下代碼

//類中
public event GreetingDelegate MakeGreet;
//Main方法中
 //gm.MakeGreet = EnglishGreeting;//錯誤    1    事件「ConsoleDemo.GreetingManager.MakeGreet」只能出如今 += 或 -= 的左邊(從類型「ConsoleDemo.GreetingManager」中使用時除外)    

 

private GreetingDelegate MakeGreet; //對事件的聲明 實際是 聲明一個私有的委託變量
 
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_MakeGreet(GreetingDelegate value){
    this.MakeGreet = (GreetingDelegate) Delegate.Combine(this.MakeGreet, value);
}

[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_MakeGreet(GreetingDelegate value){
    this.MakeGreet = (GreetingDelegate) Delegate.Remove(this.MakeGreet, value);
}

MakeGreet事件確實是一個GreetingDelegate類型的委託,只不過無論是否是聲明爲public,它老是被聲明爲private

另外,它還有兩個方法,分別是add_MakeGreet和remove_MakeGreet,這兩個方法分別用於註冊委託類型的方法和取消註冊。

      實際上也就是: 「+= 」對應 add_MakeGreet,「-=」對應remove_MakeGreet。而這兩個方法的訪問限制取決於聲明事件時的訪問限制符

在add_MakeGreet()方法內部,實際上調用了System.Delegate的Combine()靜態方法,這個方法用於將當前的變量添加到委託鏈表中。

咱們前面提到過兩次,說委託其實是一個類,在咱們定義委託的時候:public delegate void GreetingDelegate(string name);

當編譯器遇到這段代碼的時候,會生成下面這樣一個完整的類:

public sealed class GreetingDelegate:System.MulticastDelegate{
    public GreetingDelegate(object @object, IntPtr method);
    public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);
    public virtual void EndInvoke(IAsyncResult result);
    public virtual void Invoke(string name);
}

參考一個小程序理解下事件和委託結合使用

大綱圖以下:

myEventSource                        3.引起事件的類        事件 在內部觸發,外部綁定
       myEventArgs    - Class       1.提供事件數據的類
      myEventHandler - delegate  2.事件委託
myEventListener 監聽事件的類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleDemo
{
    //事件功能由三個互相聯繫的元素提供的,1.提供事件數據的類,2.事件委託,3.引起事件的類
    //發佈事件的類
    public  class myEventSource//3.引起事件的類
    {
        public class myEventArgs : EventArgs //1.提供事件數據的類,來至System.EventArgs
        {
            public  string KeyToRaiseEvent = "init value"; //添加了一個只讀屬性KeyToRaiseEvent
            public myEventArgs(string keyToRaiseEvent)
            {
                KeyToRaiseEvent = keyToRaiseEvent;
            }
        }
        //在myEventSource中添加的委託和事件
        public delegate void myEventHandler(object sender, myEventArgs e);//2.事件委託
        public event myEventHandler myEvent;//聲明一個事件

        public void OnmyEvent(string keyToRaiseEvent)//引起事件的方法 只有此方法調用的時候纔會進行控制檯輸出 在內部觸發,外部綁定
        {
            myEventArgs e = new myEventArgs(keyToRaiseEvent);
            if (myEvent != null)
            {
                myEvent(this, e);//事件的觸發
            }
            else
            {
                Console.WriteLine("事件初次綁定  eventSource 實例化  myEvent爲null");
            }
               
        }
    }
    //監聽事件的類   感受這個類狠多餘
    public class myEventListener
    {
        public void KeyPressed(object sender, myEventSource.myEventArgs e)//處理事件的方法,這個方法必須與委託的簽名一致
        {
            Console.WriteLine("發送者爲:{0},所按的鍵爲:{1}", sender, e.KeyToRaiseEvent);
        }
        //訂閱事件
        public void Subsribe(myEventSource eventSource)
        {
            //把KeyPressed這個方法「綁定」到委託上,而後再經過+=將該委託添加到源對象的事件中  綁定事件處理程序
            eventSource.myEvent += new myEventSource.myEventHandler(KeyPressed);
        }
        //取消訂閱事件
        public void UnSubsribe(myEventSource eventSource)
        {
            eventSource.myEvent -= new myEventSource.myEventHandler(KeyPressed);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            myEventSource eventSource = new myEventSource();//這裏傳遞的是一個事件源
            myEventListener eventListener = new myEventListener();

            eventSource.OnmyEvent("");
            eventListener.Subsribe(eventSource);//訂閱事件 也就是添加委託引用  這裏沒有任何值 只是添加了引用 
            
            eventSource.OnmyEvent("  空格");
            string s = Console.ReadLine();//讀入一個字符串
            eventSource.OnmyEvent(s);//引起事件

            eventListener.UnSubsribe(eventSource);
            s = Console.ReadLine();
            eventSource.OnmyEvent(s);
            Console.ReadKey();
            //運行結果
            //a
            //發送者爲:ConsoleApplication1.myEventSource,所按的鍵爲:a
            //a
        }
    }
}
    
相關文章
相關標籤/搜索