.net的委託和事件的直接理解 (轉)

worldblog發表於2007-08-17
.net的委託和事件的直接理解 (轉)[@more@]

 

  初學者在理解委託和事件時常常被msdn搞糊塗,為了讓初學的人應用.net的委託和事件模型,我在這裡主要是提出理解的關鍵,以下程式碼都可直接執行,先看下面的程式碼。

using System;

namespace delegeteTest
{
 
  class delegeteClass
   { 

 public delegate void fHandler(int a);  //關鍵-此行可以看成類的宣告
 public  fHandler f0;
   public void d(int a,int b )
 {
   int c=a+b;
   f0(c);
   }
 }
  class test
   {
 public void output(int mun)
   {
 System.Console .WriteLine ("{0}",mun);
 }
 
   [STAThread]
  static void Main(string[] args)
  {
     test t=new test ();
   delegeteClass dc=new delegeteClass ();
   dc.f0 =new  delegeteTest.delegeteClass.fHandler (t.output);//例項的初始化
 dc.d(2,3);
  }
   }
}

解釋一下"關鍵": 實際上 public delegate void fHandler(int a);可以看成如下:

 class fHandler 
 {.....}

  類內部由自動完成,是一個sealed類透過反可以看到,是一個類的宣告,它檢查加入自己的的資訊,如,返回值和引數型別

現在熟悉vc++的人可能感覺到public delegate void fHandler(int a);這句就象一個宏 

現在好了既然是個類的定義,那麼也可以直接拿到名稱空間下了 

  using System;

namespace delegeteTest
{
 public delegate void fHandler(int a);//fHandler現在上升到了類的層次
 
 class delegeteClass
 {
 public  fHandler f0;//宣告瞭委託fHandler的例項f0;

  public fHandler  f1;//也可以再宣告一個fHandler類的例項f1;
 public void d(int a,int b )
 {
 int c=a+b;
 f0(c);
 }
 }
 class test
 {
 public void output(int mun)
 {
 System.Console .WriteLine ("{0}",mun);
 }
 
 [STAThread]
 static void Main(string[] args)
 {
 test t=new test ();
 delegeteClass dc=new delegeteClass ();
 dc.f0 =new  delegeteTest.fHandler (t.output);//此行做相應的修改
 dc.d(2,3);
 }
 }
}

  有以上可知public delegate void fHandler(int a)這行程式碼只能放在能夠宣告類的地方,自然fHandler.後面的函式都是靜態方法了,如fHandler.Equals (...);

  那麼fHandler到底宣告瞭什麼? 實際上是宣告瞭函式的型別,既函式的有關資訊(如返回值,引數型別)。說到委託還是要說一下委託型別的例項。在msdn中的很多地方,(委託)這個詞指的是委託型別的例項,它擁有了一個列表,列表的每一項包含了函式資訊和函式所在的的引用。

 在宣告fHandler類的例項f0的時候,f0還不能用,是空的,所以f0需要初始化 dc.f0 =new  delegeteTest.fHandler (t.output); 初始化的引數包含了兩個資訊 t--物件的引用,output--函式資訊,如果把初始化的這句註釋掉,你執行一下看有什麼資訊----“未將物件引用設定到物件的例項”。另外output函式的引數和返回值需要和fHandler的類宣告一致,這是由編譯器在編譯時檢查的。

 經過初始化之後  現在例項中有了一項資料(實際上大多數只有一項,這樣比較高,也就是single cast的,此時例項是Delegate型別的(注意是大寫的D))。

 現在說一下委託的多播(multi cast),實際上委託的多播就是把列表裡的每一項函式一次),但是多播的效率不是很高的所以委託的大部分例項都是單播(single cast),另外可能委託的例項會根據列表內函式的個數來執行不同的機制(這裡我們就沒必要研究它了)。看下面的程式碼:

namespace delegeteTest
{
  public delegate void fHandler(int a);
 
  class delegeteClass
  {
  public  fHandler f0;
   public  fHandler f1;
   public void d(int a,int b )
   {
   int c=a+b;
 f1(c); //合併指標列表的多播委託
 }
  }
  class test
  {
  public void output(int mun)
   {
  System.Console .WriteLine ("  函式1顯示{0}",mun);
 }
 public void output1(int mun)
  {
   System.Console .WriteLine ("  函式2顯示{0}",mun);
   }
 
   [STAThread]
   static void Main(string[] args)
   {
   test t=new test ();
   delegeteClass dc=new delegeteClass ();
   dc.f0 =new  delegeteTest.fHandler (t.output);
  dc.f1 =new fHandler (t.output1 );
 
  System.Console .WriteLine ("第一次觸發");
  dc.d (2,3);//第一次觸發
 
 dc.f1 =(fHandler)Delegate.Combine (dc.f0 ,dc.f1 );//合併生成新的]函式列表
  dc.f1 =(fHandler)MulticastDelegate.Combine (dc.f1 ,dc.f0 );//同上
  System.Console .WriteLine ("第二次觸發");
   dc.d(2,3);//第二次觸發
 }
  }
}
實際上dc.f1 =(fHandler)MulticastDelegate.Combine (dc.f1 ,dc.f0 );和dc.f1 =(fHandler)Delegate.Combine (dc.f1 ,dc.f0 );效果是一樣的;由上面的例子可知我們完全可以由delegate 來構造我們自己的事件。為了方便大家進行程式設計,為我們提供了event。
using System;

namespace delegeteTest
{
 public delegate void fHandler(int a);
 
 class delegeteClass
 {
 public  fHandler f0;
 public  fHandler f1;
 public  event fHandler f3; //關鍵--實際上此行也可以看成一個宏

 public void d(int a,int b )
 {
 int c=a+b;
 f3(c);  // 改由event進行
 }
 }
 class test
 {
 public void output(int mun)
 {
 System.Console .WriteLine ("  第1顯示{0}",mun);
 }
 public void output1(int mun)
 {
 System.Console .WriteLine ("  第2顯示{0}",mun);
 }
 
 [STAThread]
 static void Main(string[] args)
 {
 test t=new test ();
 delegeteClass dc=new delegeteClass ();
 dc.f0 =new  delegeteTest.fHandler (t.output);
 dc.f1 =new fHandler (t.output1 );
 
 dc.f3 +=new fHandler(t.output); //f3是event
 System.Console .WriteLine ("第一次觸發");
 dc.d (2,3);//第一次觸發

  System.Console .WriteLine ("第二次觸發");
 dc.f3 +=(fHandler)Delegate.Combine (dc.f0 ,dc.f1 );//新增列表到f3
 dc.d(2,3);//第二次觸發
 }
 }
}

透過ildasm反彙編可以看到public  event fHandler f3宏宣告瞭兩個成員

 .field private class delegeteTest.fHandler f3和對f3的一個包裝:

.event delegeteTest.fHandler f3
{
  .addon instance void delegeteTest.delegeteClass::add_f3(class delegeteTest.fHandler)
  .removeon instance void delegeteTest.delegeteClass::remove_f3(class delegeteTest.fHandler)
},

 所以上面紅色的程式碼可以替換成如下:

  private  fHandler _f3;//由於不能與屬性名相同,所以用_f3,是私有
 public  event fHandler f3
 {
   add
  {
   this._f3 += value;
 }
   remove
   {
 this._f3  -= value;
   }
 }

event 關鍵字方便了大家的習慣,同時它(由event定義的事件)在類的外部使用時隱藏了它的成員函式(這一點非常重要,幾乎就是使用event關鍵字的原因),並且只能用“+=”和“-=”來操作,除此之外和直接宣告public fHandler f3沒有區別。它可以和上面的f0,f1一樣允許有各種引數和返回值(EventHandler是一個特定的委託,沒有返回值,引數是和EventArgs類(或繼承)的)。是在委託基礎上的。實際上我們都能用delegate 來構造我們想要的事件,這就是delegate 在.net裡的地位很高的原因。

   下面我就說說一般的事件模型

 我先談一下控制元件裡的"On事件名"這個函式 如:OnTextChange(EventArgs e),類似的有很多,實際上這是一種程式設計方面的習慣,他代表的是引發這個TextChanged事件(當然和上面一樣,包含一個委託onTextChanged和一個event包裝),具體的說就是在Text變數已經被賦值後的一個函式OnTextChanged(),OnTextChanged()裡面包含了類似onTextChanged(e)這樣的引發事件的程式碼。如果你重寫了OnTextChanged(),而沒有在(重寫的函式)裡新增base.OnTextChanged(e);那麼你在外部新增的TextChanged事件的處理,永遠也不會觸發到,也就是TextChanged事件失效了。

 

以上只是幫助大家理解,因為這樣才能事半功倍,尤其上面的關鍵,可以省了大家不少力氣。希望專家批評指正
 
 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-963788/,如需轉載,請註明出處,否則將追究法律責任。

相關文章