事件和委托的區別


前言:作為.Net攻城獅,你面試過程中是否遇到過這樣的問題呢:什么是事件?事件和委托的區別?既然事件作為一種特殊的委托,那么它的優勢如何體現?諸如此類…你是否也曾經被問到過?你又是否都答出來了呢?

關於面試中涉及到的事件的問題,我們只需要抓住幾個關鍵點就好了:

(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我們可以看到:

c# 基礎事件

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

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

//私有委托變量
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);
}

 

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

c# 基礎系列

base.DoubleClick轉到定義:

c#基礎系列

Events.AddHandler()轉到定義:

c#基礎系列

是不是很眼熟,也是通過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的時候就會打印提示文件改變。

c#基礎系列

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

//public event FileWatchEventHandler FileWatchEvent;
public FileWatchEventHandler FileWatchEvent;

 

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

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

c#基礎系列

這里正是需要說明的上面的面試回答第三條:事件只能通過+=和-+去閹割委托,而不能主動觸發事件。如當使用事件的機制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變量,除了+=和-=操作,其他所有操作都會報錯:

c#基礎系列

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

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


免責聲明!

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



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