天天看点

【原创】ASP.NET开发 笔试题目详解系列(三)

    出处:http://blog.csdn.net/caoxicao

    作者:草惜草

    转载请注明出处

问题四 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[]

 接下来我们就可以直接使用calculation调用sub方法了:

calculation

(10,3);

当然也可以新建

委托数组

,如下就建立了一个委托数组Calculation[]

 接下来我们就可以直接使用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);      

   }

}

我们创建一个与代理签名相匹配的静态函数或成员函数,然后用 += 向事件中添加代理的一个新实例