程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 事件的解密,事件解密

事件的解密,事件解密

編輯:C#入門知識

事件的解密,事件解密


在前面一篇中寫到了委托,也說了委托是C#中很多特性的基礎,這篇要講的事件,就是建立在委托之上的。在C#1.0中,委托和事件是最重要的兩個特性。

1、什麼是事件?

事件設計到兩類角色——事件發布者和事件訂閱者。當某個事件發生後,事件發布者會發布消息;事件訂閱者會接收到信息,並做出相應的處理,這就是事件的過程。

 

2、使用事件

2.1 定義事件

在C#中定義事件和定義類的成員是很相似的,只要一個event關鍵字就可以了。比如:

public event EventHandler birthday;

其中event是關鍵字,而EventHandler是委托類型。

所以可以把事件定義的結構總結為:訪問修飾符 event 委托類型 事件名;其中委托類型可以是自定義的委托類型,也可以是.NET類庫中預定義的委托類型EventHandler。

2.2 訂閱和取消事件

事件訂閱者需要訂閱事件發布者發布的事件消息,以便在事件被觸發式接收消息並做出相應處理。在C#中,可以使用“+=”來訂閱事件,使用“-=”來取消訂閱事件。

public class Bridegroom
{
  //自定義委托
  public delegate void MarryHandler(string msg);
  //使用自定義委托類型定義事件,事件名為MarryEvent
  public event MarryHandler MarryEvent;

  //發出事件
  public void OnMarriageComing(string msg)
  {
    //判斷是否綁定了事件處理方法
    if(MarryEvent!=null)
    {
      //觸發事件
      MarryEvent(msg);
    }
  }


  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();

    //實例化朋友對象
    Friend friend1=new Friend("張三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");

    //使用“+=”來訂閱事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

    //發出通知,此時只有訂閱了事件的對象才能收到通知
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");

    //使用"-="來取消事件訂閱,此時李四將收不到通知
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

    bridegroom.OnMarriageComing("Friend,I Will Marry!!");

    Console.ReadKey();
  }
}

  public class Friend
  {
    public string Name;
    public Friend(string name)
    {
      Name=name;
    }
    //事件處理函數,該函數需要符合MarryHandler委托的定義
    public void SendMessage(string message)
    {
      Console.WriteLine(message);
      Console.WriteLine(this.Name+"收到了,到時候准時參加");
    }
  }

值得注意的是,事件處理函數的定義需要與自定義的委托定義保持一致,即參數個數,參數類型和返回類型等需要與委托相同。

除了使用自定義委托類型來定義事件外,還可以使用.NET類庫中預定義的委托類型EventHandler來定義事件,需要注意它們的參數。

public class Bridegroom
{
  //使用.NET類庫中的類型定義事件,事件名為MarryEvent
  public event EventHandler MarryEvent;

  //發出事件
  public void OnMarriageComing(string msg)
  {
    //判斷是否綁定了事件處理方法
    if(MarryEvent!=null)
    {
      Console.WriteLine(msg);
      //觸發事件
      MarryEvent(this,new EventArgs());
    }
  }


  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();

    //實例化朋友對象
    Friend friend1=new Friend("張三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");

    //使用“+=”來訂閱事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

    //發出通知,此時只有訂閱了事件的對象才能收到通知
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");

    //使用"-="來取消事件訂閱,此時李四將收不到通知
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

    bridegroom.OnMarriageComing("Friend,I Will Marry!!");

    Console.ReadKey();
  }
}

public class Friend
{
  public string Name;
  public Friend(string name)
  {
    Name=name;
  }
  //事件處理函數,該函數需要符合MarryHandler委托的定義
  public void SendMessage(object s,EventArgs e)
  {
    Console.WriteLine(this.Name+"收到了,到時候准時參加");
  }
}

EventHandler是.NET類庫中預定義的委托類型,用於處理不包含事件數據的事件。使用Reflector來查看EventHandler的具體定義:

[Serializable, ComVisible(true), __DynamicallyInvokable]
public delegate void EventHandler(object sender, EventArgs e);

從定義中可以看出,該委托類型的返回類型為void,第一個參數sender負責保存觸發事件對象的引用,其類型為object;第二個參數e負責保存事件數據。EventArgs類也是.NET類庫中定義的類,它不保存任何數據,如果想在事件中包含事件數據,就必須使用EventArgs的派生類來實現。

2.3 擴展EventArgs類

上面說了,如果要在事件中包含事件數據,就必須使用EventArgs的派生類。具體的實現代碼如下:

public class MarryEventArgs:EventArgs
{
  public string Message;
  public MarryEventArgs(string msg)
  {
    Message=msg;
  }
}

public class Bridegroom
{
  //自定義委托類型,委托包含兩個參數
  public delegate void MarryHandler(object sender,MarryEventArgs e);
  //使用自定義委托類型定義事件,事件名為MarryEvent
  public event MarryHandler MarryEvent;
  //發出事件
  public void OnMarriageComing(string msg)
  {
    //判斷是否綁定了事件處理方法
    if(MarryEvent!=null)
    {
      //觸發事件
      MarryEvent(this,new MarryEventArgs(msg));
    }
  }


  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();
    //實例化朋友對象
    Friend friend1=new Friend("張三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");
    //使用“+=”來訂閱事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);
    //發出通知,此時只有訂閱了事件的對象才能收到通知
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");
    //使用"-="來取消事件訂閱,此時李四將收不到通知
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);
    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.ReadKey();
  }
}

public class Friend
{
  public string Name;
  public Friend(string name)
  {
    Name=name;
  }
  //事件處理函數,該函數需要符合MarryHandler委托的定義
  public void SendMessage(object s,MarryEventArgs e)
  {
    Console.WriteLine(e.Message);
    Console.WriteLine(this.Name+"收到了,到時候准時參加");
  }
}

通過自定義MarryEventArgs事件類擴展了EventArgs類,此時MarryEventArgs帶有一個名為Message的事件參數;然後在訂閱對象的SendMessage方法中,通過e.Message的方式獲得了事件數據,並把事件數據輸出。

 

3、事件的本質

從以上的例子我們可以知道,事件是在委托的基礎之上的。那麼,它們到底有著什麼樣的關系呢,這個就必須通過Reflector來窺探了。

簡單的源代碼:

namespace 窺探事件本質
{
  class Program
  {
    public delegate void MarryHanler(string msg);

    public event MarryHanler MarryEvent;
    static void Main(string[] args)
    {
    }
  }
}

Reflector反編譯的結果:

圖1

 

圖2

 

圖3

圖4

可以看出,C#事件被編譯成包含兩個公共方法的代碼段,一個帶有add_前綴,另一個帶有remove_前綴,前綴後面是C#事件的名稱。

在add_方法中,通過調用了Delegate.Combine()方法來實現的(圖3中紅框的地方),Delegate.Combine()方法將多個委托組合為了一個多路廣播委托。

在remove_方法中,同樣采用了Delegate.Remove()方法。

由上面的四張圖中可以總結出:

C#的事件是一個特殊的多路廣播委托,事件默認含有一個私有的委托類型變量(圖2的紅框),該變量用於保存對事件處理方法的引用,且該委托類型的變量為私有,只能從定義該事件的類中進行訪問。

從反編譯的代碼中可以看出跟我們學過的屬性是相似的。但與事件不同,屬性中定義了set訪問和get訪問器,兩個訪問器的本質就是以"get_"和"set_"為前綴的兩個方法。屬性用於對類中的私有字段進行訪問,而C#事件也可以看作是“委托字段的屬性”,因此可以通過事件來對私有的委托字段進行訪問,這也是C#事件特性存在的原因。C#事件機制符合面向對象的封裝特性,是代碼更安全。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved