天天看點

ASP.NET開發 筆試題目詳解(3)——委托和事件

問題四: C#中的委托是什麼?事件是不是一種委托?

答:委托:委托是C#中的一種引用類型,類似于C/C++中的函數指針。與函數指針不同的是,委托是面向對象、類型安全的,而且委托可以引用靜态方法和執行個體方法,而函數指針隻能引用靜态函數。委托主要用于 .NET Framework 中的事件處理程式和回調函數。

 一個委托可以看作一個特殊的類,因而它的定義可以像正常類一樣放在同樣的位置。與其他類一樣,委托必須先定義以後,再執行個體化。

委托派生于基類System.Delegate,不過委托的定義和正常類的定義方法不太一樣。委托的定義通過關鍵字delegate來定義:

public delegate int myDelegate(int x,int y);
           

上面的代碼定義了一個新委托,它可以封裝任何傳回為int,帶有兩個int類型參數的方法。任何一個方法無論是執行個體方法還是靜态方法,隻要他們的簽名(參數類型在一個方法中的順序)和定義跟委托是一樣的,都可以把他們封裝到委托中去。

産生委托執行個體和産生類執行個體(對象)差不多,假如我們有如下的方法:

public int sub(int x,int y){
       return(x+y);
}
           

我們就可以使用如下的代碼得到一個委托執行個體:

myDelegate calculatin=new myDelegate(sub); // 用方法名!!!
           

接下來我們就可以直接使用calculation調用sub方法了:

calculation(10,3);
           

當然也可以建立委托數組,如下就建立了一個委托數組Calculation[]

       private delegate int Calculation(int a, int b);

       private static Calculation[] myCalculation=new Calculation[2];
           

此時,可以用委托數組中的成員來封裝待委托方法,實作建立委托執行個體。

如:

myCalculation[0]=new Calculation(MathClass.max); // 方法名
myCalculation[1]=new Calculation(MathClass.min); // 方法名
           

這樣,也就可以用建立的委托執行個體數組,來直接調用方法了。

事件:在C#中,委托的最基本的一個用處就是用于事件處理。是對象發送的消息,以發信号通知操作的發生,通俗一點講,事件就是程式中産生了一件需要處理的信号。

事件的定義用關鍵字 event 聲明,不過聲明事件之前必須存在一個多路廣播委托(一個委托同時委托多個方法):

public delegate void Calculate(int x,int y);//傳回值為void的委托自動成為多路廣播委托;
public event  Calculate OnCalculate;
           

可以看出,事件的聲明僅僅是比委托執行個體的聲明多了個關鍵字event,事實上事件可以看作是一個為事件處理過程定制的多路廣播委托。是以,定義了事件後,我們就可以通過向事件中操作符+=添加方法實作事件的預定或者是通過操作符-=取消一個事件,這些都與委托執行個體的處理是相同的。

下面再單獨談談 事件代理 的相關概念及其應用:

我們就一起來模仿一個Button類及Button值改變後引發事件并執行自定義的ButtonTextChangeEvent事件處理函數的過程,進而以事件資料得到該Button指派過多少次。

Using System;
namespace MyServerControlTest { 
   public delegate void ButtonTextChangeEventHander ( object sender , EventArgs e ); //事件委托的聲明。 
   public class Button { 
   public event ButtonTextChangeEventHander TxtChange;//定義一個事件委托的引用,以備外部進行事件的綁定. 
   private string _Text;; 
   public string Text { 
            get { return _Text;; } 
           set { 
                     _Text = value;; 
                     System.EventArgs e = new EventArgs();; //這裡是事件的基類執行個體. 
                     ChangeTxt(e);; //調用事件處理函數. 
                } 
   } 
       private void ChangeTxt(EventArgs e)  { //事件處理函數. 
       if( TxtChange != null ) 
           TxtChange(this,e);;//真正調用外部指派的處理函數. 
   } 
   } 
}
           

為什麼要這樣定義委托聲明: 

delegate ButtonTextChangeEventHander (Object sender , EventArgs e )
           

原因也很簡單,為了符合.net framework CLS的約定:

Sender :引發事件的對象 ; e:事件所帶的資料,而之是以用EventArgs e 是因為這裡的事件是無資料的,如果自定義具有資料的事件的話,還是要從System.EventArgs基類中繼承。

需要指出的是:

if( TxtChange != null ) 

   TxtChange(this,e);;
           

如果不加 if( TxtChange != null ) 

那麼當TxtChange并沒有綁定事件處理函數時,它将會引發“未将對象引用到執行個體”的Exception。

   /**//**//** 〈summary〉 
   /// 事件處理類 
   /// 〈/summary〉 
   public class DelegateDemo { 
   //定義一個執行個體 
   private Button button;

   public DelegateDemo(){ 
   InitializeComponent();
   //構造函數調用時,就開始操作button進行指派。 
   SetValues();

} 

   public void InitializeComponent(){ 
   button = new Button();; 
   //對button對象的TxtChange事件引用,進行事件與處理函數的綁定。 
   //前面已經說過了,EventHandler是一個類,是以要用new進行建立它的執行個體,其主要是把button_TxtChange事件處理函數的位址傳至這個委托執行個體。
   button.TxtChange += new ButtonTextChangeEventHander(button_TxtChange);; 
   }
   public void SetValues() { 
   string[] values = {"AAA","BBB","CCC","DDD","EEE"};; 
             for( int i = 0;; i 〈 5;; i++ ) { 
            //進行指派,進而引發事件而直接調用button_TxtChange事件處理函數。 
             button.Text = values[i];; 
            } 
   }
  private void button_TxtChange(object sender, System.EventArgs e) { 
   Console.WriteLine( " Button執行個體新值。其值為:" + ((Button)sender).Text ); 
   } 
} 
   public class MyMain  { 
            public static void Main() { 
             DelegateDemo delegateDemo = new DelegateDemo();; 
             Console.Read();; 
        } 
   } 
}
           

感覺還是講的不是很清楚。再來看一個:

public class MyObject
{
   public delegate void ClickHandler(object sender, EventArgs e);
   public event ClickHandler Click;
   protected void OnClick()
   {
      if (Click != null)
         Click(this, null);
   }
}
           

ClickHandler 代理使用事件代理的标準模式來定義事件的簽名。其名稱的末尾是處理程式,它帶有兩個參數。第一個參數是發送對象的對象,第二個參數用于傳遞事件的伴随資訊。這種情況下沒有要傳遞的資訊,是以直接使用 EventArgs,但是如果有資料要傳遞,則使用從 EventArgs 派生的類(例如 MouseEventArgs)。

“Click”事件的聲明執行兩項操作:首先,它聲明一個名為“Click”的代理成員變量,該變量從類的内部進行使用。其次,它聲明一個名為“Click”的事件,該事件可按照正常通路規則從類的外部進行使用。

通常,将包括 OnClick() 等函數,以便使類型或派生類型能夠觸發事件。由于“Click”是代理,您将會注意到,用來觸發事件的代碼與代理的代碼相同。

與代理類似,我們使用 += 和 -= 來挂接到事件或解除事件挂接,但與代理不同的是,僅可對事件執行這些操作。這可確定不會發生先前所讨論的兩種錯誤。

使用事件是一種直截了當的方法。

我們建立一個與代理簽名相比對的靜态函數或成員函數,然後用 += 向事件中添加代理的一個新執行個體:

class Test
{
   static void ClickFunction(object sender, EventArgs args)
   {
      // process the event here.
   }
   public static void Main()
   {
      MyObject myObject = new MyObject();

      myObject.Click += new MyObject.ClickHandler(ClickFunction);      
   }
}
           

繼續閱讀