C#基礎---事件的使用


   一:什么是事件

       事件是可以被控件識別的操作,如按下確定按鈕,選擇某個單選按鈕或者復選框。每一種控件有自己可以識別的事件,如窗體的加載、單擊、雙擊等事件,編輯框(文本框)的文本改變事件,等等。事件在桌面應用程序里面無處可見,比如winform,WPF。。。,其次事件是基於委托而產生的。

        二:事件的基本使用

     1.事件的聲明: 其實和委托一樣只是多了一個Event而已。ShowMsg 就具備了ShowMsgHandler的功能。

    Notes: 1. 委托可以依賴於一個類或者一個域名空間(C#基礎---委托的使用,里面我有提到過), 而event必須依賴於一個類。否者無法聲明。

         2. 委托可以用【=號】,而事件中只能用【+】或者【-】實現對方法的添加和刪除。當事件為空的時候調用【-】方法不會報錯。

        

public delegate void ShowMsgHandler(string str);
public event ShowMsgHandler ShowMsg;

     2.事件基本使用: 其實基本用法和委托差不多。這里有一點要說明,其實可以通過判斷是否為Null,來確定是否已經注冊了方法到事件或者委托。這個有時候勇於判斷事件是否該觸發。其次也發現事件第一次添加方法的時候是直接使用【+】號的,而不用像委托那樣第一次使用【=】號,后面的才使用【+】號。         

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

namespace SpongeBobCoder.EventTest
{
    public delegate void ShowMsgHandler(string str);
    public class Program
    {
        public static event ShowMsgHandler ShowMsg;

        public static void ShowName(string str)
        {
            Console.WriteLine("My Name is {0}", str);
        }

        public static void Main(string[] args)
        {
            Console.WriteLine(ShowMsg == null);
            ShowMsg += ShowName;
            ShowMsg("SopongeBob");
            Console.WriteLine(ShowMsg == null);
            Console.ReadKey();
        }
    }
}

      3. 為何使用事件:

             其實從上面來看事件和委托差不多。用法沒啥區別,但是為何還要使用委托呢? 在Codeplex看到的一篇文檔感覺挺不錯的。http://www.codeproject.com/Articles/7316/Events-and-Delegates ;小弟英語不好,英語好的就去看看原文,我的大概理解是: 好比一個App由一個項目組在開發,必定有一個項目經理,而這個項目經理的下面會有很多幫他做事的員工,其實經理就好比是一個委托,而每一位工作的員工就好比委托注冊的方法。項目開發完成了,拿給了用戶,用戶安裝好了。可是用戶發現軟件有缺陷,需要改進,而往往這個時候用戶是不會直接跟開發組的項目經理接觸的。往往會把意見給 維護部門,而由維護部門來告知相應開發組的項目經理。而通知這段過程就叫做事件。事件可以用來更好的封裝,和管理委托的。個人理解,這就是事件為何基於特定類的。不同類的 事件可以綁定同一個委托,從而注冊不同的方法。

       3.1 先什么一個Publisher類:類中有一個事件CalculatorEvent 和一個方法DoSomething,若事件被添加方法后,那么將執行

    

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

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1,double num2)
        {
            if (CalculatorEvent != null)
            {
                CalculatorEvent(num1, num2);
            }
        }
    }
}

    3.2 然后看Program類,Main方法里面聲明了兩個Publisher對象,分別是A,B。分別添加了AddNum和SumNum,運行結果是 3 和 -1。通過一個Publisher類可以對委托進行管理。

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

namespace SpongeBobCoder.EventTest
{
    public delegate void CalculatorHandler(double num1,double num2);

    public class Program
    {
        public static void AddNum(double num1, double num2)
        {
            Console.WriteLine("兩數之和為:{0}", num1 + num2);
        }

        public static void SubNum(double num1, double num2)
        {
            Console.WriteLine("兩數之差為:{0}", num1-num2);
        }

        public static void Main(string[] args)
        {
            Publisher pubA = new Publisher();
            Publisher pubB = new Publisher();
            pubA.CalculatorEvent += AddNum;
            pubB.CalculatorEvent += SubNum;

            pubA.DoSomething(1, 2);
            pubB.DoSomething(1, 2);

            Console.ReadKey();
        }
    }
}

     三:事件的使用

    1.異常處理: 事件可以注冊多個方法,可是要是其中有一個方法拋出異常了怎么辦,一旦拋出異常了。那么當前執行程序被中斷,那么后面注冊的方法就沒法執行了。問題來了,那些方法可以解決這個問題呢?  方法一,保證所注冊的方法不會出現異常,這一塊我們是無法預知。 方法二。將注冊的方法的異常都吃掉,其中微軟提供了兩個方法GetInvocationList和DynamicInvoke方法:

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

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1, double num2)
        {
            if (CalculatorEvent != null)
            {
                Delegate[] delArray = CalculatorEvent.GetInvocationList(); //獲取到所有的委托方法.
                foreach (Delegate del in delArray)
                {
                    try
                    {
                       object obj = del.DynamicInvoke(num1, num2); //obj是獲取每個方法的返回值,如果聲明的是無返回值的委托,那么obj==null
                       Console.WriteLine(obj == null);
                    }
                    catch (Exception e) // 把異常吃掉
                    {
                        Console.WriteLine(e.InnerException.Message); 
                    }
                }
            }
        }
    }
}

  2.異步調用:對於前面的注冊的時間,都是順序執行,那如何實現異步執行呢,各個注冊之間不相互干擾,其實微軟提供了一個BeginInvoke方法可以解決這個問題.

Public IAsyncResult BeginInvoke (
    InvokeArgs invokeArgs, // 這一部分對於的是委托
    AsyncCallback callback,// 回調方法,注冊方法執行完后,將會執行回調方法
    Object userState // 傳遞的參數
)

            上面理解起來可能有點困難下面來看看代碼吧:
            Program類: AddNum 方法有5秒的延時。SubNum沒有添加延時,注冊順序是 AddNum,SubNum

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

namespace SpongeBobCoder.EventTest
{
    public delegate double CalculatorHandler(double num1, double num2);

    public class Program
    {
        public static double AddNum(double num1, double num2)
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine("兩數之和為:{0}", num1 + num2);
            return num1 + num2;
        }

        public static double SubNum(double num1, double num2)
        {
            Console.WriteLine("兩數之差為:{0}", num1 - num2);
            return num1 - num2;
        }


        public static void Main(string[] args)
        {
            Publisher pubA = new Publisher();
            pubA.CalculatorEvent += AddNum;
            pubA.CalculatorEvent += SubNum;
            pubA.DoSomething(1, 5);
            Console.ReadKey();
        }
    }
}

    publisher類: 注意看紅色方法,前面兩個參數是與委托對應的。后面的MyCallBack是回調的方法,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1, double num2)
        {
            if (CalculatorEvent != null)
            {
                Delegate[] delArray = CalculatorEvent.GetInvocationList(); //獲取到所有的委托方法.
                foreach (Delegate del in delArray)
                {
                    try
                    {
                        CalculatorHandler handler = del as CalculatorHandler;
                        IAsyncResult myResult = handler.BeginInvoke(num1, num2, MyCallback, "方法執行完畢,回調成功" + handler.Method.Name);                         // Console.WriteLine("SpongeBob"); 這塊代碼不注釋的話是先執行這段代碼的輸出,然后才會輸出其他的。這一塊我不知道為什么
                    }
                    catch (Exception e) // 把異常吃掉
                    {
                        Console.WriteLine(e.InnerException.Message);
                    }
                }
            }
        }

        public void MyCallback(IAsyncResult asyncResult)
        {
            AsyncResult result = (AsyncResult)asyncResult;
            CalculatorHandler handler = (CalculatorHandler)result.AsyncDelegate;
            Console.WriteLine(asyncResult.AsyncState);
            Console.WriteLine("獲取到執行結果為:{0} \n", handler.EndInvoke(asyncResult));
        }
    }
}

        運行結果: 其實先執行的是 SubNum,已經達到了異步的效果,其中通過EndInvoke也在回調函數中獲取到了委托的返回值。
     

      codezip:http://files.cnblogs.com/FourLeafCloverZc/CSharp.zip

總結:  以前最開始的對事件理解不清楚,記得當時在winform的時候跨線程獲取數據就要用Invoke來獲取,不然老是提示線程不安全。本次博客中我發現了兩個問題需要個各位博友幫忙解答

 1. 在異步調用的代碼中有一段代碼被我注解了。其實我發現了一個問題, 如果 Console.WriteLine("SpongeBob"); 不注解,運行的情況是,先輸出兩行(“SpongeBob”) 然后再輸出 上圖的運行結果。不清楚是什么原因,求大神指點。

 2. 對於下面代碼如果調用的傳入的參數是1,0 ; 是不會報錯的。運行結果是兩數相除無窮大。應該會報除數不能為0的異常呀。

        public static void DivNum(double num1, double num2)
        {
            Console.WriteLine("兩數相除為:{0}", num1 / num2);
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM