.Net之美讀書系列(二):委託進階

此次看書的知識點:異步

  1. 事件訪問器
  2. 若是一個委託中註冊了多個事件且須要獲取其返回值的方法
  3. 委託的異常處理
  4. 委託處理超時的方法
  5. 異步委託

 

事件訪問器async

職能有:ide

1.對委託屬性進行封裝,再也不直接該委託變量直接進行-=和+=的操做,
2.保護委託變量只能支持註冊一個事件,不能註冊多個事件
3.從而即便委託中定義了返回值也不會被覆蓋函數

仍是直接上代碼吧:spa

        public static void Main(string[] args)
        {
            Publishser publish = new Publishser();
            Subscriber subber = new Subscriber();
            Subscriber subber2 = new Subscriber();

            publish.NumberChanged += subber.OnNumberChange;
            publish.NumberChanged += subber2.OnNumberChange;//會把上面的覆蓋,最終只會註冊最後的事件

            publish.DoSomething();
            Console.Read();
        }


    }

    public delegate string GeneralEventHandler();

    public class Publishser
    {
        //對委託屬性進行封裝,再也不直接該委託變量直接進行-=和+=的操做,
        //保護委託變量只能支持註冊一個事件,不能註冊多個事件
        //從而即便委託中定義了返回值也不會被覆蓋
        private GeneralEventHandler numberChanger;

        //傳說中的事件訪問器,value屬性是系統字段
        public event GeneralEventHandler NumberChanged { add { numberChanger = value; } remove { //即便沒有賦值也不會報錯
                numberChanger -= value; } } public void DoSomething()
        {
            if (numberChanger != null)
            {
                //調用帶返回值的委託變量
                string returnvalue = numberChanger();
                Console.WriteLine("返回值:" + returnvalue);
            }
        }
    }

    public class Subscriber
    {
        public string OnNumberChange()
        {
            string num = "2";
            Console.WriteLine("輸入" + num);
            return num;
        }
    }

 

獲取委託中多個返回值線程

若是一個委託變量中註冊了多個事件,那麼須要逐個調用而且return對應的返回值,這時候可使用List集合進行裝載而後返回code

        public static void Main(string[] args)
        {
            Subscriber sub1 = new Subscriber();
            Subscriber sbu2 = new Subscriber();
            Subscriber sbu3 = new Subscriber();
            Publisher pub = new Publisher();
            //註冊事件,注意:這裏是直接對事件進行註冊,並無使用事件訪問器
            pub.numberChanged += sub1.sub;
            pub.numberChanged += sbu2.sub;
            pub.numberChanged += sbu3.sub;

            //觸發事件
           List<string> resultList= pub.DoSomething(10);
           foreach (var item in resultList)
           {
               Console.WriteLine(item);
           }
            Console.Read();
        }

        public delegate string DoneSomeThingEventHander(int num);

        /// <summary>
        /// 發佈者
        /// </summary>
        public class Publisher
        {
            //定義事件
            public event DoneSomeThingEventHander numberChanged;

            public List<string> DoSomething(int parmeter)
            {
                List<string> resultList = new List<string>();
                if (numberChanged == null)
                {
                    return resultList;
                }

                //按照調用順序獲取多路廣播的列表(就是註冊過的事件s)
                Delegate[] delArray = numberChanged.GetInvocationList(); foreach (var item in delArray) { //由於委託其實編譯成一個類,並且都是繼承與Delegate,因此能夠強制轉換爲對應的委託
                    DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item; //讀取訂閱者的返回值
 resultList.Add(menthod(parmeter)); } return resultList;
            }
        }

        /// <summary>
        ///訂閱者
        /// </summary>
        public class Subscriber
        {
            public string sub(int num)
            {
                Console.WriteLine(num);
                return num.ToString();
            }
        }

若是直接調用事件,那麼就是觸發全部的註冊的事件,返回值將會是最後一個事件的返回值,可是若是使用了事件的GetInvocationList()方法,那麼其實就能夠獲取到曾經註冊過的事件,而後根據註冊的順序逐一調用,就能夠肯定但返回值了.對象

 

委託的異常處理blog

若是一個委託變量,按照順序註冊了ABC個方法,調用完A後,B出錯了,那麼C就壓根不會被執行,換句話說,3個訂閱者,若是中間的訂閱者報錯了,那麼後面的訂閱者也沒辦法執行下去.若是直接在方法中try掉也是沒法執行下去的.因此只能使用上面的GetInvocationList()獲取到全部註冊事件,而後逐個try一次,這樣就不影響調用了.繼承

        public static void Main(string[] args)
        {
            Subscriber sub1 = new Subscriber();
            Subscriber2 sbu2 = new Subscriber2();
            Subscriber3 sbu3 = new Subscriber3();
            Publisher pub = new Publisher();
            //註冊事件,注意:這裏是直接對事件進行註冊,並無使用事件訪問器
            pub.numberChanged += sub1.sub;
            pub.numberChanged += sbu2.sub;
            pub.numberChanged += sbu3.sub;

            //觸發事件
           List<string> resultList= pub.DoSomething(10);
           foreach (var item in resultList)
           {
               Console.WriteLine(item);
           }
            Console.Read();
        }

        public delegate string DoneSomeThingEventHander(int num);

        /// <summary>
        /// 發佈者
        /// </summary>
        public class Publisher
        {
            //定義事件
            public event DoneSomeThingEventHander numberChanged;

            public List<string> DoSomething(int parmeter)
            {
                List<string> resultList = new List<string>();
                if (numberChanged == null)
                {
                    return resultList;
                }

                ////若是直接try掉,程序能夠走下去,可是訂閱者卻不能訂閱到事件
                //try
                //{
                //  resultList.Add(  numberChanged(parmeter));
                //}
                //catch (Exception ex)
                //{
                //    return new List<string>();
                //}

                //第二種異常的處理方式
                Delegate[] delArray = numberChanged.GetInvocationList();
                foreach (var item in delArray)
                {
                    //這樣try掉的話,就不影響其餘的訂閱者了
                    try
                    {
                        DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item;
                        resultList.Add(menthod(parmeter));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                return resultList;
            }
        }

        /// <summary>
        ///訂閱者
        /// </summary>
        public class Subscriber
        {
            public string sub(int num)
            {
                Console.WriteLine(num);
                return num.ToString();
            }
        }

        /// <summary>
        ///訂閱者2
        /// </summary>
        public class Subscriber2
        {
            public string sub(int num)
            {
                throw new Exception("我是訂閱者2的錯誤");
                return string.Empty;
            }
        }

        /// <summary>
        ///訂閱者2
        /// </summary>
        public class Subscriber3
        {
            public string sub(int num)
            {
                Console.WriteLine("沒報錯");
                return "沒報錯";
            }
        }
    }
View Code

 

訂閱者方法超時的處理方式(異步調用)

訂閱者除了能夠經過異常的方式來影響發佈者之外,還能夠經過另外一種方式:超時。超時和異常的區別就是超時並不會影響事件的正確觸發和程序的正常運行,卻會致使事件觸發後須要很長才可以結束。能作。由於當執行訂閱者方法時(經過委託,至關於依次調用全部註冊了的方法),當前線程會轉去執行方法中的代碼,調用方法的客戶端會被中斷,只有當方法執行完畢並返回時,控制權纔會回到客戶端,從而繼續執行客戶端接下來的代碼.

委託的定義會生成繼承自MulticastDelegate的完整的類,其中包含Invoke()、BeginInvoke()和EndInvoke()方法。Invoke()直接調用委託阻斷客戶端,BeginInvoke()和EndInvoke()則是一個配套的方法,BeginInvoke()執行完以後,客戶端就不用再管,會啓動一個閒置的線程執行訂閱者的方法,

        public static void Main(string[] args)
        {
            Subscriber sub1 = new Subscriber();
            Subscriber2 sbu2 = new Subscriber2();
            Subscriber3 sbu3 = new Subscriber3();
            Publisher pub = new Publisher();
            //註冊事件,注意:這裏是直接對事件進行註冊,並無使用事件訪問器
            pub.numberChanged += sub1.sub;
            pub.numberChanged += sbu2.sub;
            pub.numberChanged += sbu3.sub;

            //觸發事件
            List<string> resultList = pub.DoSomething(10);
            foreach (var item in resultList)
            {
                Console.WriteLine("最後輸出結果的");
            }
            Console.WriteLine("客戶端的最後");
            Console.Read();
        }

        public delegate string DoneSomeThingEventHander(int num);

        /// <summary>
        /// 發佈者
        /// </summary>
        public class Publisher
        {
            public event DoneSomeThingEventHander numberChanged;
            public List<string> DoSomething(int parmeter)
            {
                List<string> resultList = new List<string>();
                if (numberChanged == null)
                {
                    return resultList;
                }
                //若是註冊的事件有多個,那麼只能逐個調用BeginInvoke(),不然會報錯
                Delegate[] delArray = numberChanged.GetInvocationList();
                foreach (var item in delArray)
                {
                    try
                    {
                       DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item; //參數爲null的,書中是遲點再討論,
                        menthod.BeginInvoke(parmeter, null, null);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                return resultList;
            }
        }

        /// <summary>
        ///訂閱者
        /// </summary>
        public class Subscriber
        {
            public string sub(int num)
            {
                Thread.Sleep(1000);
                Console.WriteLine("睡了1秒");
                return "睡了1秒";
            }
        }

        /// <summary>
        ///訂閱者2
        /// </summary>
        public class Subscriber2
        {
            public string sub(int num)
            {
                Thread.Sleep(2000);
                Console.WriteLine("睡了2秒");
                return "2秒";
            }
        }

        /// <summary>
        ///訂閱者2
        /// </summary>
        public class Subscriber3
        {
            public string sub(int num)
            {
                Console.WriteLine("沒睡");
                return "沒睡";
            }
        }
    }

以上只是一個最簡單的最簡單的不阻斷主線程的方法,其實完成的方法應該是配合EndInvoke()一塊兒使用的.調用BeginInvoke()就是至關於開了一個後臺線程去運行方法,只要主線程不關掉,那麼後臺線程也會繼續執行知道它執行完成爲止.

 

異步委託

1.使用異步委託,解決等待問題,而且如何獲取委託中處理完結果的方法

    class Program
    {


        static void Main(string[] args)
        {
            Console.WriteLine("客戶端開啓");

            Calculator c = new Calculator();
            //委託變量
            AddEventHandler addmenthod = new AddEventHandler(c.Add);

            //addmenthod += c.Add;//若是要異步調用,調用的委託變量只能註冊一個方法,不然會報錯


            //在調用委託時,begininvoke會動態組成參數,前面的參數就是方法的參數了
            IAsyncResult asyncResult = addmenthod.BeginInvoke(2, 5, null, null);//不使用委託調用完後回調函數,
            for (int i = 1; i <= 3; i++)
            {
                Thread.Sleep(i * 1000);
                Console.WriteLine("{0}: Client executed {1} second( s).", Thread.CurrentThread.Name, i);

            }

            //若是想獲取委託的返回值,則使用EndInvoke,若是是在同一個方法中固然可使用,原來委託的對象,若是不是在同一個方法中,則能夠經過 IAsyncResult asyncResult   這個返回值來得到委託對象
            //int delegateresult = addmenthod.EndInvoke(asyncResult);

            //在別的方法中處理得到異步委託處理的結果(和上面的是同樣的,只是在第二個地方獲取返回的結果而已)
            int delegateresult = c.getRusult(asyncResult);


            Console.WriteLine("委託執行結果"+delegateresult);

            Console.WriteLine(Thread.CurrentThread.Name + "程序執行完成");
            Console.Read();
        }
    }

    //定義個對應的須要異步調用的方法的委託
    public delegate int AddEventHandler(int x, int y);

    public class Calculator
    {
        public int Add(int x, int y)
        {
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";

            }
            Console.WriteLine("方法線程開始執行");
            for (int i = 1; i <= 2; i++)
            {
                Thread.Sleep(i * 1000);
                Console.WriteLine(Thread.CurrentThread.Name + "執行了" + i + "");
            }
            Console.WriteLine("方法執行完了");
            return x + y;
        }

        /// <summary>
        /// 定義一個方法獲取返回的結果
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public int getRusult(IAsyncResult asyncresult)
        {
            //強制轉換爲AsyncResult對象(多態的緣由)
            AsyncResult result = (AsyncResult)asyncresult;
            //獲取原來委託的對象並強制轉換
            AddEventHandler del = (AddEventHandler)result.AsyncDelegate;
            int returnresult = del.EndInvoke(asyncresult);
            return returnresult;
        }
    }
View Code

若是要在別的地方獲取委託的結果,只須要傳入委託begininvoke返回的 IAsyncResult asyncResult對象,參照  public int getRusult(IAsyncResult asyncresult)便可.

 

2.若是在委託沒有執行完,可是又調用了委託的EndInvoke()方法的話,就會阻塞主線程,那麼咱們不知道何時纔會執行調用完,這時候在調用beginInvoke的倒數第二個參數就是傳入一個若是方法結束了的委託的對象了.

    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("客戶端開啓");

            Calculator c = new Calculator();
            //委託變量
            AddEventHandler addmenthod = new AddEventHandler(c.Add);

            int parmeter = 123;
            AsyncCallback callback = new AsyncCallback(c.getRusult);
            //此次傳入倒數第二個參數,就是一個回調函數,,這個回調函數就是當委託執行完以後回調的方法,倒數第一個參數能夠在委託中拿取到
            //callback是一個以IAsyncResult爲參數的,返回值爲void的方法的委託
            IAsyncResult asyncResult = addmenthod.BeginInvoke(2, 5, callback, parmeter);
            for (int i = 1; i <= 3; i++)
            {
                Thread.Sleep(i * 1000);
                Console.WriteLine("{0}: Client executed {1} second( s).", Thread.CurrentThread.Name, i);

            }

            Console.WriteLine(Thread.CurrentThread.Name + "程序執行完成");
            Console.Read();
        }
    }

    //定義個對應的須要異步調用的方法的委託
    public delegate int AddEventHandler(int x, int y);

    public class Calculator
    {
        public int Add(int x, int y)
        {
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";

            }
            Console.WriteLine("方法線程開始執行");
            for (int i = 1; i <= 2; i++)
            {
                Thread.Sleep(i * 1000);
                Console.WriteLine(Thread.CurrentThread.Name + "執行了" + i + "");
            }
            Console.WriteLine("方法執行完了");
            return x + y;
        }

        /// <summary>
        /// 定義一個方法獲取返回的結果
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public void getRusult(IAsyncResult asyncresult)
        {
            //強制轉換爲AsyncResult對象(多態的緣由)
            AsyncResult result = (AsyncResult)asyncresult;
            //獲取原來委託的對象並強制轉換
            AddEventHandler del = (AddEventHandler)result.AsyncDelegate;
            int returnresult = del.EndInvoke(asyncresult);
            Console.WriteLine("委託執行完了,輸出傳入的參數"+result.AsyncState.ToString());
        }
    }
View Code

 

 此次就到這裏,最大的收穫應該是對委託的理解吧

相關文章
相關標籤/搜索