出处: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”是代理,您将会注意到,用来触发事件的代码与代理的代码相同。
与代理类似,我们使用 += 和 -= 来挂接到事件或解除事件挂接,但与代理不同的是,仅可对事件执行这些操作。这可确保不会发生先前所讨论的两种错误。
使用事件是一种直截了当的方法。
我们创建一个与代理签名相匹配的静态函数或成员函数,然后用 += 向事件中添加代理的一个新实例