C#基礎系列——再也不用擔心面試官問我“事件”了


前言:作為.Net攻城獅,你面試過程中是否遇到過這樣的問題呢:什么是事件?事件和委托的區別?既然事件作為一種特殊的委托,那么它的優勢如何體現?諸如此類...你是否也曾經被問到過?你又是否都答出來了呢?上兩篇由淺及深介紹了下委托的用法,這篇還是來說說事件。希望通過這篇的介紹,博友能有個系統的認識,至少應付面試沒問題了吧。不信?瞧瞧去~~

C#基礎系列目錄:

 

  開篇博主也不多說廢話了,翠花,上答案。。。關於面試中涉及到的事件的問題,我們只需要抓住幾個關鍵點就好了:

(1)事件是委托的封裝,可以理解為一種特殊的委托。

(2)事件里面其實就兩個方法(即add_event()和remove_event())和一個私有的委托變量,這兩個方法里面分別是對這個私有的委托變量進行的合並和移除,當調用事件的+=時其實是調用的事件里面的add_event()方法,同樣-=調用的是remove_event()方法。

(3)事件只能夠從對象外部增加新的響應方法和刪除已知的響應方法,而不能主動去觸發事件和獲取其他注冊的響應方法等信息。如果使用公有的delegate則不能做這些限制,也就是說事件對委托做了限制,使委托使用起來更加方便。也有人說事件是對委托的閹割,大概也是這個意思。

如果回答的時候抓住了以上的3點,那么我想你的面試應該不會太差。畢竟面試那么短的時間,有一兩個亮點就很不錯了,你說呢。哪怕你對事件機制完全不懂,為了面試記住其中兩點也是很好的,工作經驗咱們沒有,換工作的經驗可不能沒有哦~~扯遠了,關於面試就到此為止。如果你還想繼續將事件了解透徹,別着急,慢慢往下看。

 

1、事件的定義及由來:

定義事件:

    public delegate void MyStudyEvent(object sender, EventArgs e);
    public class TestEvent
    {
        public event MyStudyEvent eMyStudyEvent;
    }

將這段代碼生成dll后,通過反編譯工具reflector我們可以看到:

正如上文所說,可以看到當定義一個事件public event MyStudyEvent eMyStudyEvent的時候,編譯器會自動給他生成兩個方法add和remove,以及一個private的委托變量eMyStudyEvent。我們將反編譯代碼copy出來看看。

     //私有委托變量
        private MyStudyEvent eMyStudyEvent;

        //add方法合並委托到eMyStudyEvent里面
        public void add_eMyStudyEvent(MyStudyEvent value)
        {
            MyStudyEvent event3;
            MyStudyEvent eMyStudyEvent = this.eMyStudyEvent;
            do
            {
                event3 = eMyStudyEvent;
                MyStudyEvent event4 = (MyStudyEvent)System.Delegate.Combine(event3, value);
                eMyStudyEvent = Interlocked.CompareExchange<MyStudyEvent>(ref this.eMyStudyEvent, event4, event3);
            }
            while (eMyStudyEvent != event3);
        }

        //remove方法移除eMyStudyEvent里面已存在的委托
        public void remove_eMyStudyEvent(MyStudyEvent value)
        {
            MyStudyEvent event3;
            MyStudyEvent eMyStudyEvent = this.eMyStudyEvent;
            do
            {
                event3 = eMyStudyEvent;
                MyStudyEvent event4 = (MyStudyEvent)System.Delegate.Remove(event3, value);
                eMyStudyEvent = Interlocked.CompareExchange<MyStudyEvent>(ref this.eMyStudyEvent, event4, event3);
            }
            while (eMyStudyEvent != event3);
        }

可以看到這兩個方法的主要作用就是在向private變量eMyStudyEvent里面添加委托和移除委托。當調用事件的+=和-=時,eMyStudyEvent里面就合並和移除傳過來的委托,當事件觸發的時候,eMyStudyEvent變量就執行。這樣設計也正好符合封裝的原則,保證了內部變量的安全性。

 

2、Framework里面的事件:既然自定義的事件是這樣的,那么有人就要問了,.Net里面的事件是否也是如此。我們直接通過反編譯工具來看。我們找到System.Windows.Forms下面的Button類,這個也是我們Winform里面使用最多的按鈕,我們來看看我們經常使用的事件。

base.DoubleClick轉到定義:

Events.AddHandler()轉到定義:

 

是不是很眼熟,也是通過Delegate.Combine()來合並委托。

 

3、自定義事件的使用。事件使用的例子園子里面文章也很多,此片博主就以監聽文件夾里面的1.txt文件是否存在為例說明事件的使用以及使用事件和委托的區別。

首先定義事件以及觸發事件的方法:

  

   public
delegate void FileWatchEventHandler(object sender, EventArgs e); public class FileWatch { private bool _bLastStatus = false; public FileWatch() { } public event FileWatchEventHandler FileWatchEvent; protected virtual void OnFileChange(EventArgs e) { if (FileWatchEvent != null) { FileWatchEvent(this, e); } }
     //事件監聽的方法
public void MonitorFile() { bool bCurrentStatus; while (true) { bCurrentStatus = File.Exists(@"C:\Users\user\Desktop\桌面文件夾\1.txt"); if (bCurrentStatus != _bLastStatus) { _bLastStatus = bCurrentStatus; OnFileChange(EventArgs.Empty); } Thread.Sleep(250); } } }

然后在Main函數里面啟動監聽以及定義事件處理方法:

     static FileWatch FileWatchEventSource;

        static void Main(string[] args)
        {
            FileWatchEventSource = new FileWatch();
            //1. 啟動后台線程添加監視事件
            var thrd = new Thread(new ThreadStart(FileWatchEventSource.MonitorFile));
            thrd.IsBackground = true;
            thrd.Start();


            //2.注冊本地事件處理方法
            FileWatchEventSource.FileWatchEvent += new FileWatchEventHandler(OnFileChange);

            Console.ReadLine();
        }

        private static void OnFileChange(object Sender, EventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + ": 文件發生改變.");
        }

啟動程序后,每當在文件夾里面刪除和創建1.txt的時候就會打印提示文件改變。

 

博主好奇心重,貌似這種監聽直接用委托也可以實現了。於是乎將event事件變量直接改成委托變量。

//public event FileWatchEventHandler FileWatchEvent;
public FileWatchEventHandler FileWatchEvent;

然后運行。發現可以得到一樣的結果。

只是在反編譯的時候沒有add和remove方法而已:

這里正是需要說明的上面的面試回答第三條:事件只能通過+=和-+去閹割委托,而不能主動觸發事件。如當使用事件的機制public event FileWatchEventHandler FileWatchEvent時。在Main函數里面

static void Main(string[] args)
        {
            FileWatchEventSource = new FileWatch();
            //1. 啟動后台線程添加監視事件
            var thrd = new Thread(new ThreadStart(FileWatchEventSource.MonitorFile));
            thrd.IsBackground = true;
            thrd.Start();


            //2.注冊本地事件處理方法
            
            FileWatchEventSource.FileWatchEvent += new FileWatchEventHandler(OnFileChange);


         //這樣寫是報錯的。
            FileWatchEventSource.FileWatchEvent(null, null);

            Console.ReadLine();
        }

這樣寫是會報錯的FileWatchEventSource.FileWatchEvent(null, null);因為事件不能主動觸發,而改成委托后這樣寫就是正確的。並且如果是event變量,除了+=和-=操作,其他所有操作都會報錯:

從這里可以看出,事件對委托進行了封裝和約束。

 

  總而言之,言而總之,事件和委托,打一個不太恰當的比喻,就類似面包和面粉,面包是由面粉加工而來的,當我們肚子餓了的時候,面包直接就能吃,面粉還需要加工。而當我們需要面條的時候,面粉就派上用場了,面包對於我們來說是用不了的。不知道這樣解釋好理解否。說了這么多,確實有點繞,需要好好體會下。

 


免責聲明!

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



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