1. 程式人生 > 程式設計 >簡單聊聊c# 事件

簡單聊聊c# 事件

引言:

前面幾個專題對委託進行了詳細的介紹的,然後我們在編寫程式碼過程中經常會聽到“事件”這個概念的,尤其是寫UI的時候,當我們點選一個按鈕後VS就會自動幫我們生成一些後臺的程式碼,然後我們就只需要在Click方法裡面寫程式碼就可以,所以可能有些剛接觸C#的朋友就覺得這樣很理所當然的,也沒有去思考這是為什麼的,為什麼點選下事件就會觸發我們在Click方法裡面寫的程式碼呢?事件到底扮演個什麼樣的角色呢?為了解除大家的這些疑惑,下面就詳細介紹了事件,讓一些初學者深入理解C#中的事件的概念。

一、為什麼C#中會有事件的?

  前面專題中介紹了我理解的為什麼需要委託的,所以這裡我來分享下我理解的為什麼C#中要引入事件這個概念的。下面就簡單講講生活中事件的例子的,最近我生日剛過完的,我就以生日這個話題要談談的,日子一天天的過去,當生日的日期到的時候,這時候就觸發了生日事件的,此時過生日的人就是觸發生日事件的物件的,然後有些關係你的朋友就會對這個事件進行關注,一旦這個事件觸發, 他們就可能會陪你一起慶祝生日,然後送禮物給你,當然並不是所有人都會對你的生日關注的,有些人肯定根本就不知道的, 只有對於你生日事件進行了關注了的人才會送禮物給你。這樣的生活中的一個生日過程,然而對於為什麼C#中會有事件這個概念當然就更好理解了,C#是一個面向物件的語言,我們使用C#語言進行編碼也是為了用程式碼幫助我們完成現實生活中的事情的,所以當然也就必須有事件來反映生活中發生事情的情況了。

二、自己如何實現一個事件模式的?

  現在我們知道了為什麼C#要引入事件了,但是對於我們在程式碼中使用的事件大部分都是.net類庫為我們提供的,例如控制元件的各種事件,我們只需要點選按鈕後就會觸發點選事件的,但是我們很想理解這個事件是如何觸發的,我們是否可以自己定義實現事件模式的一個程式的呢?答案當然是可以的,下面就以上面生日的例子來通過程式碼來解釋下如何實現一個事件模式的。

具體程式碼為:

using System;
using System.Threading;

namespace BirthdayEventDemo
{
  class Program
  {
    static void Main(string[] args)
    {
      // 例項化一個事件源物件
      Me eventSource = new Me("Learning Hard");

      // 例項化關注事件的物件
      Friend1 obj1 = new Friend1();
      Friend2 obj2 = new Friend2();

      // 使用委託把物件及其方法註冊到事件中
      eventSource.BirthDayEvent+=new BirthDayEventHandle(obj1.SendGift);
      eventSource.BirthDayEvent+=new BirthDayEventHandle(obj2.Buycake);
      
      // 事件到了觸發生日事件,事件的呼叫
      eventSource.TimeUp();
      Console.Read();
    }
  }
  // 第一步: 定義一個型別用來儲存所有需要傳送給事件接收者的附加資訊
  public class BirthdayEventArgs : EventArgs
  {
    // 表示過生日人的姓名
    private readonly string name;

    public string Name
    {
      get { return name;}
    }

    public BirthdayEventArgs(string name)
    {
      this.name = name;
    }
  }
  // 第二步:定義一個生日事件,首先需要定義一個委託型別,用於指定事件觸發時被呼叫的方法型別
  public delegate void BirthDayEventHandle(object sender,BirthdayEventArgs e);
  // 定義事件成員
  public class Subject
  {
    // 定義生日事件
    public event BirthDayEventHandle BirthDayEvent;

    // 第三步:定義一個負責引發事件的方法,它通知已關注的物件(通知我的好友)
    protected virtual void Notify(BirthdayEventArgs e)
    {
      // 出於執行緒安全的考慮,現在將對委託欄位的引用複製到一個臨時欄位中
      BirthDayEventHandle temp = Interlocked.CompareExchange(ref BirthDayEvent,null,null);
      if (temp != null)
      {
        // 觸發事件,與方法的使用方式相同
        // 事件通知委託物件,委託物件呼叫封裝的方法
        temp(this,e);
      }
    }
  }

  // 定義觸發事件的物件,事件源
  public class Me : Subject
  {
    private string name;
    public Me(string name)
    {
      this.name = name;
    }
    public void TimeUp()
    {
      BirthdayEventArgs eventarg = new BirthdayEventArgs(name);
      // 生日到了,通知朋友們
      this.Notify(eventarg);

    }
  }

  // 好友物件
  public class Friend1
  {
    public void SendGift(object sender,BirthdayEventArgs e)
    {
      Console.WriteLine(e.Name+" 生日到了,我要送禮物");
    }
  }
  public class Friend2
  {
    public void Buycake(object sender,BirthdayEventArgs e)
    {
      Console.WriteLine(e.Name + " 生日到了,我要準備買蛋糕");
    }
  }
}

執行結果為:

簡單聊聊c# 事件

三、編譯器是如何解釋事件的呢?

  上面我們已經介紹瞭如何去實現自己去實現一個事件模式的,大家可以展開程式碼來具體的檢視的,實現過程主要是——定義觸發物件的事件源(指的是誰過生日)->定義關注你生日事件的朋友物件-> 方法登記對事件的關注,當事件觸發時通知登記的方法被呼叫。然而相信大家還有有疑問——到底C#中的事件是什麼呢?編譯器又是如何去解釋它的?下面就為大家解除下疑惑的:

  首先事件其實就是委託的(確切的說事件就是委託鏈),從上面的程式碼中,我們定義的事件除了使用event關鍵字外,還用到了一個委託型別,然而委託是一個類,類肯定就有屬性欄位的,然而我們就可以把事件理解為委託的一個屬性,屬性的返回值是一個委託型別。說事件是委託的一個屬性,是有根據的,我們通過中間語言程式碼可以知道編譯器是如何去解釋我們定義的事件的。

 // 第二步:定義一個生日事件,首先需要定義一個委託型別,用於指定事件觸發時被呼叫的方法型別
  public delegate void BirthDayEventHandle(object sender,BirthdayEventArgs e);
  // 定義生日事件
    public event BirthDayEventHandle BirthDayEvent;

當我們像上面定義一個事件時,編譯器會把它轉換為3段程式碼(大家可以通過IL反彙編程式來檢視的):

// 1. 一個被初始化為null的私有委託欄位
    private BirthDayEventHandle BirthDayEvent =null;

    //2. 一個公共add_BirthDayEvent方法
    public void add_BirthDayEvent(BirthDayEventHandle value)
    {
      // 以一種執行緒安全的方式從事件中新增一個委託
    }
    // 3. 一個公共的remove_BirthDayEvent方法
    public void remove_BirthDayEvent(BirthDayEventHandle value)
    {
      // 以一種執行緒安全的方式從事件中移除一個委託
    }

第一段程式碼一個委託的私有欄位,該欄位是對一個委託列表的頭部的引用,事件發生時會通知這個列表中的委託。欄位初始化為null,表明無關注人登記了對事件的關注。
第二段程式碼是一個以add為字首的方法,該方法是由編譯器自動命名的,程式碼內容呼叫Delegate.Combine方法將委託例項新增到委託列表中,返回新的列表地址,並將這個地址存回字段。

第三段程式碼也是一個方法,它使得一個物件登出對事件的關注,同樣的方法體呼叫Delegate.Remove方法將委託例項從委託列表中刪除,返回新的列表地址,並將這個地址存回字段中。(注,如果試圖刪除一個從未新增過的方法,Delegate.Remove方法在內部將不做任何事情,也就是說,不會丟擲任何一次,也不會顯示任何警告,事件的方法集合保持不變)。

同時大家也可以通過除錯來說明事件是一個委託鏈的,大家可以在 eventSource.BirthDayEvent+=new BirthDayEventHandle(obj2.Buycake);這行程式碼設定一個斷點除錯的,下面是我除錯過程中的一個截圖,大家也可以自己除錯看看的,這樣將會更加理解事件是一個委託鏈的概念:

簡單聊聊c# 事件

按F10執行一行後的截圖

簡單聊聊c# 事件

通過上面的截圖,相信大家對於事件是一個委託鏈的概念相信會有進一步的理解的。

四、小結

  到這裡本專題的內容也就介紹完了, 希望通過本專題,大家可以對事件有進一步的理解,理解事件與委託之間的關係。這個專題通過自己實現的一個事件模式裡解釋事件的本質,然而我們經常使用的是Net類庫中定義好的事件,然而有些剛接觸C#的人卻不理解Net中定義的事件背後所做的事情,只是知道點下按鈕後在Click方法裡面寫入自己的一些控制程式碼,然而背後的過程具體是怎樣的,既然事件是委託,那麼Click事件是委託型別,其中的委託型別又是怎麼被例項化的呢?這些內容將在下一個專題給大家分享下的。

以上就是簡單聊聊c# 事件的詳細內容,更多關於c# 事件的資料請關注我們其它相關文章!