.Net 机制下委托(一) 事件委托
oldjacky原创 2005-6-4
一、 认识委托
一个简单的例子:
张三看到餐桌上有一个桔子,由于自己怕动(主要是出于自己要玩游戏,走不开),立刻就对着他妈妈喊:“我要吃桔子,妈妈帮我拿过来。”,接着,他妈妈听到乖儿子要吃桔子,就立刻送去给儿子了。
从某种意义上来说,把儿子发出消息要桔子的动作
与妈妈送桔子给儿子的动作相关联的过程就称为
委托,也就是说儿子发出消息要桔子这个事件委派妈妈根据他的消息内容去完成他想要做的事。
二、 理解.net机制下的委托概念
关于委托的声明:
delegate
返回类型 方法声明( 参数1,参数2,参数3… );
例如: delegate void ButtonTextChangeEvent( object sender, EventArgs e );
例如: delegate void ButtonTextChangeEvent( object sender, EventArgs e );
注意:声明一个ButtonTextChangeEvent,就是创建了一个“委托类”,在编译期间,就会产生个委托类在代码区,因而它可以在类里面声明,也可以跟类在同一域(即命名空间中定义),享受与类同等待遇,那么在这个类里面会有一个最关键的函数就是
virtual
返回类型 方法声明(参数1,参数2,参数3…);
图示:
那么这个Ivoke虚函数是用做干什么的呢?它主要是用来调用某个绑定了的函数,说白了,delegate充其量就是一个中间件 (相当于我们所见的媒婆)。
namespace
ConsoleApplicationTest
{
//声明一个委托(如果您学过C++那么它很像C++里面的typedef.)
public delegate int FunPtr();
public class A
{
//定义一个委托类型的变量(类型:引用)
public FunPtr ptr;
}
public class Controller
{
public void AdelegateTest()
{
A a = new A();
a.Ptr = new FunPtr(Fun); //对a实例中的ptr进行委托绑定
}
public int Fun(){}
}
}
{
//声明一个委托(如果您学过C++那么它很像C++里面的typedef.)
public delegate int FunPtr();
public class A
{
//定义一个委托类型的变量(类型:引用)
public FunPtr ptr;
}
public class Controller
{
public void AdelegateTest()
{
A a = new A();
a.Ptr = new FunPtr(Fun); //对a实例中的ptr进行委托绑定
}
public int Fun(){}
}
}
声明的委托它是类类型(请记住),定义一个委托类型的变量,主要是为了指向某个静态函数的首地址或是实例的中某个函数首地址。,
委托是面向对象的、类型安全的和保险的,它是对对象的引用以及对该对象内一个或多个方法的引用组成(即多路广播),那么何为多路广播呢?多路广播就是它可以把委托叠加到一起,形成一个委托链表的形式,进行依次调用。
形式:对象.委托变量
+=
new
委托类( 委托类中invoke的函数声明形式的函数式 )
举例:button. ptr += new FunPtr (Fun);
举例:button. ptr += new FunPtr (Fun);
关键是
“ += ” 形成了一个委托链 ,也可以把它理解成是向委托数据表中添加函数地址,调用时按委托数据表中的顺序依次转至函数式地址进行执行(顺序:先进先出),当然您也可以用
“ -= ”来对委托数据表中的函数地址进行移除操作。
**.委托在 .net下面,虽然类似于C++下面的函数指针,但它比C++更安全,更有效,更容易使用。
三、事件委托
好,现在我们对委托有一定的了解了,现在开始事件委托的历程。
简单的说,事件委托就是在对象内进行事件声明,而在该对象之外用事件处理函数与对象声明的事件进行绑定,然后,该对象事件的引发,继而执行相应的事件处理函数的一个过程,例如:Button的OnClick事件。
好,既然说到Button的事件,那我们就一起来模仿一个Button类及Button值改变后引发事件并执行自定义的ButtonTextChangeEvent事件处理函数的过程,从而以事件数据得到该Button赋值过多少次。
我们先来看看这个过程是如何传递的:
传递过程:
Button – > 赋值 – > 引发ButtonTextChangeEvent – > 调用相应的处理程序
Button – > 赋值 – > 引发ButtonTextChangeEvent – > 调用相应的处理程序
好,现在这流程已经有了,那么,我们一步步来做:
namespace
ConsoleApplicationTest
{
public class Button
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
{
public class Button
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
到目前为止,一个基本的Button类设计完成(这个就不解释了,谁都知道是啥意思。),它已经具有了赋值的功能,接下来,我们来看第三步,既然要引发事件,那么我们必须得有事件的声明,让事件处理委派于外部处理函数(事件绑定),当对Button.Text赋值时进行引发事件.
namespace
ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
好,现在事件的声明与Button的事件的引用也定义了,为了达到Button.Text赋值时引发事件,进行那么现在就到需要在set{…}里面进行事件调用的代码编写:
namespace
ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander ( object sender , System.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(System.EventArgs e)
{
if( TxtChange != null )
TxtChange(this,e);//真正调用外部指派的处理函数.
}
}
}
{
public delegate void ButtonTextChangeEventHander ( object sender , System.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(System.EventArgs e)
{
if( TxtChange != null )
TxtChange(this,e);//真正调用外部指派的处理函数.
}
}
}
delegate
ButtonTextChangeEventHander (
object
sender , System.EventArgs e )
如果不用,那不也一样可以吗?
原因也很简单,为了符合.net framework CLS的约定,所有的事件都得遵守 M S 的规定,要不然,嘿嘿,你就麻烦了,事件得不到统一的、规范的处理,所以既然用了MS.Net就遵守她的标准吧,当然在上面的那段程序中不用这样麻烦的定义也是可行的。
例如:
delegate
ButtonTextChangeEventHander (); 或者
delegate ButtonTextChangeEventHander ( int changeCount ); // 加上事件数据
delegate ButtonTextChangeEventHander ( int changeCount ); // 加上事件数据
ButtonTextChangeEventHander( object sender , System.EventArgs e );//它应该是你经常看到的吧,这种形式就是MS的一种规定,事件处理函数的一种规定,如果您喜欢,可以再加上N个参数在里面也是可以的,比如:异步回调,但这样做唯的一缺点就是不能与MS的事件相统一符合CLS的规定。
Sender :引发事件的对象 ; e:事件所带的数据
需要指出的是:
if
( TxtChange
!=
null
)
TxtChange( this ,e);
TxtChange( this ,e);
如果不加 if( TxtChange != null ) 如果TxtChange并没有绑定事件处理函数,那么它将会发“未将对象引用到实例”的Exception。
到目前为止,一个无事件数据的Button设计成型了,那么,现在我们得加上一些绑定代码,进行事件的绑定,得让Button执行赋值时,调用相应的处理函数。
using
System;
using System.Threading;
namespace ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set
<img id="Codehighlighter1_425_541_Closed_Image" style="DISPLAY: none" οnclick="this.style.display='none'; Codeh
using System.Threading;
namespace ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set
<img id="Codehighlighter1_425_541_Closed_Image" style="DISPLAY: none" οnclick="this.style.display='none'; Codeh