認識C#中的委託和事件

本文是學習 博客C# 中的委託和事件(詳解) 的心得 ,博客原文爲:http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html

委託和事件的用法:

使用委託和事件的目的,在這個例子中,加熱器(heater)只負責加熱,而報警器(alamter)負責報警和顯示器(viewer)負責顯示,而加熱器如何告訴報警器和顯示器?這就需要用到委託(事件)。

使用到了觀察者模式

爲什麼委託定義的返回值通常都爲 void ?

儘管並非必需,但是我們發現很多的委託定義返回值都爲 void,爲什麼呢?這是因爲委託變量可以供多個訂閱者註冊,如果定義了返回值,那麼多個訂閱者的方法都會向發佈者返回數值,結果就是後面一個返回的方法值將前面的返回值覆蓋掉了,因此,實際上只能獲得最後一個方法調用的返回值。可以運行下面的代碼測試一下。除此以外,發佈者和訂閱者是鬆耦合的,發佈者根本不關心誰訂閱了它的事件、爲什麼要訂閱,更別說訂閱者的返回值了,所以返回訂閱者的方法返回值大多數情況下根本沒有必要

using System;

//編碼規範
//1. 委託類型的名稱都應該以 EventHandler 結束。
//2. 委託的原型定義:有一個void 返回值,
//並接受兩個輸入參數:一個Object 類型,一個EventArgs 類型(或繼承自EventArgs)。
//3. 事件的命名爲委託去掉 EventHandler 之後剩餘的部分。
//4. 繼承自 EventArgs 的類型應該以EventArgs 結尾。
namespace testEvent
{
    class MainClass
    {
        public static void Main1 (string[] args)
        {
            Heater heater = new Heater ();
            Alamter a = new Alamter ();
            heater.sendTempure -= a.OnAlam;
            heater.sendTempure += a.OnAlam;
            heater.sendTempure += new Viewer ().OnView;
            heater.boillWater ();
        }
    }
    //監視對象,裏面含有被監視的內容:溫度
    public class Heater{
        public String Name {
            get;
            set;
        }
        public int Tempurea {
            get;
            set;
        }
        //定義成void,是爲了避免多個事件註冊而返回值被覆蓋,
        //如果有這種需求,可以用特殊方法來避免被多次註冊。
        //Object sender 用於監視者強轉成監視對象,獲取到監視對象的屬性,如Name。
        //
        public delegate void sendTempureEventHander (Object sender,BoilEventArgs e);
        public event sendTempureEventHander sendTempure;
        //這個地方必須是空的,因爲真正執行任務的是Alamter和Viewer這兩個監視者類,
        //而監視對象只是用這個handler來佔位而已    
        //要真正使sendTempure內容不爲空,需要在真正使用時,綁定監視者的具體方法。
        public void boillWater(){
            for(int i=0;i<100;i++){
                Console.WriteLine ("開始燒水");
                if(i>90){
                    BoilEventArgs boilEventArgs = new BoilEventArgs (i);
                    sendTempure (this, boilEventArgs);
                }
            }
        }
    }
    //承自 EventArgs 的類型應該以EventArgs 結尾。
    public class BoilEventArgs:EventArgs {
        public int Tempure {
            get;
            set;
        }
        public BoilEventArgs(int tempure){
            this.Tempure = tempure;
        }
    }
    //監視者
    //用於報警的類 
    public class Alamter{
        //這裏還有一個約定俗稱的規定,就是訂閱事件的方法的命名,通常爲“On 事件名”,比如這裏的OnNumberChanged
        public void OnAlam(Object sender, BoilEventArgs e){
            Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"警報器開始報警");
        }
        public static void OnAlamt(Object sender, BoilEventArgs e){
            Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"警報器開始報警");
        }
    }
    //用於顯示的類
    public class Viewer{
        public void OnView(Object sender, BoilEventArgs e){
            Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"顯示器開始顯示");
        }
    }
}

 

如何讓事件只允許一個客戶訂閱?

using System;
namespace OneEvent
{
    public class OneEventPusher{
        public String Name{ get; set;}
        public delegate void ChangeValueEventHandler(Object sender, ChangeEventArgs e);
        private event ChangeValueEventHandler changeValue;
        public void Register(ChangeValueEventHandler handler){
            changeValue = handler;
        }
        //如果changValue爲空,-=操作也不會報錯
        public void UnRegister(ChangeValueEventHandler handler){
                changeValue -= handler;
        }
        public void doWork(int i){
            //因爲在Register 方法時使用的是=,每次changeValue只會註冊一個方法,所以,可能會發生UnRegister時,changeValue爲空,故需要判斷一下
            if(changeValue!=null){
                ChangeEventArgs args = new ChangeEventArgs (i); 
                changeValue(this,args);
            }
        
        }
    }

    public class ChangeEventArgs:EventArgs{
        public int passValue {
            get;
            set;
        }
        public ChangeEventArgs(int pass_value){
            this.passValue = pass_value;
        }
    }

    public class OneEventWorker1{
        public void OnChange(Object sender,ChangeEventArgs changerArgs){
            Console.WriteLine ("I am worker 1!");
            Console.WriteLine (((OneEventPusher)sender).Name);
            Console.WriteLine (changerArgs.passValue);
        }
    }
    public class OneEventWorker2{
        public void OnChange(Object sender,ChangeEventArgs changerArgs){
            Console.WriteLine ("I am worker 2!");
            Console.WriteLine (((OneEventPusher)sender).Name);
            Console.WriteLine (changerArgs.passValue);
        }
    }

    class MainClass
    {
        public static void Main2 (string[] args)
        {
            OneEventPusher one = new OneEventPusher ();
            one.Name ="Test";
            OneEventWorker1 work1 = new OneEventWorker1 ();
            one.Register (work1.OnChange);
            one.UnRegister (work1.OnChange);
            //one.UnRegister (work2.OnChange);
            one.doWork (1);    
        }
    }
}

如何使用委託異步調用觀察者:

using System.Threading;
using System;
using System.Runtime.Remoting.Messaging;
namespace UnSynzDemo{
    public delegate void Count();
    class MainClass
    {
        //同步執行
        public static void Main3 (string[] args)
        {
            Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
            Count count = new Count (new CountClass ().getCounts);
            count() ;
            Console.WriteLine ("main Class is over");
        }
        //異步執行
        public static void Main (string[] args)
        {
            
            if (Thread.CurrentThread.IsThreadPoolThread) {
                Thread.CurrentThread.Name = "this is ThreadPoolThread";
            } else {
                Thread.CurrentThread.Name = "this is mainThread";
            }
            Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
            Counter counter = new Counter ();
            counter.doWork ();
            Thread.Sleep (5000);
            Console.WriteLine ("main Thread is over");
        }


    }
    class CountClass{
        public void getCounts(){
            int count = 0;
            if (Thread.CurrentThread.IsThreadPoolThread) {
                Thread.CurrentThread.Name = "this is ThreadPoolThread";
            } else {
                Thread.CurrentThread.Name = "this is mainThread";
            }
            Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
            for(int i =0;i<10;i++){
                count += i;
                Thread.Sleep (TimeSpan.FromMilliseconds(300));
            }
            Console.WriteLine (count);
        }
        public int OnCounts(int c){
            if (Thread.CurrentThread.IsThreadPoolThread) {
                Thread.CurrentThread.Name = "this is ThreadPoolThread";
            } else {
                Thread.CurrentThread.Name = "this is mainThread";
            }
            Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
            int totolCount = 0;
            for(int i =0;i<c;i++){
                totolCount += i;
            }
            return totolCount;
        }
    }
    //用於執行異步操作的delegate
    public delegate int CountNumEventHandler(int c);
    //系統自帶的delegate,用於綁定回調函數的方法,參數爲固定的IAsyncResult類型
    //public delegate void AsyncCallback(IAsyncResult ar);
    class Counter{
        public void doWork(){
            Console.WriteLine ("doWork Thread Name is "+ Thread.CurrentThread.Name);
            //調用BeginInvoke前面的參數和delegate的參數一致,
            //而後面兩個參數,第一個參數表示回調函數,第二個參數表示傳遞的參數(兩個參數都可以爲null值)
            //BeginInvoke的返回值爲
            int c = 10;
            string data = "123";
            CountNumEventHandler handler = new CountNumEventHandler (new CountClass().OnCounts);
            AsyncCallback callBack = new AsyncCallback (CounterCallBack);
            //後面兩個參數都可以爲null值
            //handler.BeginInvoke (c,null,null);
            //delegate 參數爲空的情況
            //handler.BeginInvoke(null,null);


            //可以直接獲取相應的運行結果和傳遞的參數
            //IAsyncResult IResult =  handler.BeginInvoke(c,callBack,data);
            //IAsyncResult IResult = handler.BeginInvoke (c,null,data);
            //AsyncResult result = (AsyncResult)IResult;
            //int i  = handler.EndInvoke (result);
            //Console.WriteLine (result.AsyncState);
            //Console.WriteLine (i);
            //Console.WriteLine ("do Working is over");



            //也可以在回調函數中獲取相應的運行結果和傳遞的參數
            IAsyncResult IResult =  handler.BeginInvoke(c,callBack,data);
        
        }
        //回調函數
        public void CounterCallBack(IAsyncResult ar){
            Console.WriteLine ("callBack Thread Name is "+ Thread.CurrentThread.Name);
            //轉爲AsyncResult,此時就可以獲取相應的屬性了
            AsyncResult result = (AsyncResult)ar;
            CountNumEventHandler handler = (CountNumEventHandler)result.AsyncDelegate;
            //獲取方法的返回值
            int rtn = handler.EndInvoke(result);
            Console.WriteLine ("CallBack return is "+rtn);
            string data = (string)result.AsyncState;
            Console.WriteLine ("CallBack passData is "+data);
            Console.WriteLine (data);
            Console.WriteLine ("CallBack Working is over");
        }
    }
}


最終異步調用的結果如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章