当前位置:文档之家› C#委托及事件

C#委托及事件

C#委托及事件在C#中,委托(delegate)是一种引用类型,在其他语言中,与委托最接近的是函数指针,但委托不仅存储对方法入口点的引用,还存储对用于调用方法的对象实例的引用。

简单的讲委托(delegate)是一种类型安全的函数指针,首先,看下面的示例程序,在C++中使用函数指针。

首先,存在两个方法:分别用于求两个数的最大值和最小值。

int Max(int x,int y){return x>yx:y;}int Min(int x,int y){return x<YX:Y;< font>}上面两个函数的特点是:函数的返回值类型及参数列表都一样。

那么,我们可以使用函数指针来指代这两个函数,并且可以将具体的指代过程交给用户,这样,可以减少用户判断的次数。

下面我们可以建立一个函数指针,将指向任意一个方法,代码如下所示:建立一个委托类型,并声明该委托可以指向的方法的签名(函数原型)delegate void MyDelegate(int a,int b);2.建立一个委托类的实例,并指向要调用的方法用委托类实例调用所指向的方法int c=md(4,5);下面通过实例来演示C#中委托的使用。

案例操作020601:利用委托实现方法的动态调用首先,添加如下控件:两个RadioButton,分别用来让用户选择求最大值以及求最小值二个TextBox,用来输入两个操作数一个TextBox,用来显示运算结果一个Button,用来执行运算界面如下图所示:下一步,在窗口中添加两个方法:Max,Min,这两方法的代码如下:int Max(int x,int y){return x>yx:y;}int Min(int x,int y){return x<YX:Y;< font>}窗口中的代码,如下图所示:下一步:为了使用委托来实现动态指向,我们需要建立一个委托类“MyDelegate”,并建立该委托类型的一个实例,如下图所示:上面的代码中,我们可以发现,此时,还没有让MyDelegate类型的实例“md”指向任何一个方法(即:md的值为null),原因是:在编写代码的时候,我们还不知道用户想要调用哪一个方法。

下一步,分别为两个RadioButton编写它们的“CheckedChanged”事件,代码如下:private void rbtMax_CheckedChanged(object sender, EventArgs e){if .Checked ==true){= new MyDelegate );}}private void rbtMin_CheckedChanged(object sender, EventArgs e){if .Checked ==true){= new MyDelegate );}}这段代码是,如果用户选择了求最大值的RadioButton,则让MyDelegate类型的实例“md”指向Max方法,如果用户选择了求最小值的RadioButton,则让MyDelegate类型的实例“md”指向Min方法。

这样作的目的,就是要把选择的过程交给用户。

下一步,我们为界面中的Button编写Click事件,并利用委托来调用求最值的方法。

代码如下所示:private void btGetResult_Click(object sender, EventArgs e){if ==null ){("委托md没有指向任何方法!");return;}int a = .Text );int b = .Text );int c = (a,b);= ();}从上面的代码中,可以发现,在使用委托之前,先要判断其值是否为空,如果不为空,则可以进行调用,同时,使用者可以看到,在调用md时,我们并没有关心md到底指向了哪一个方法,总之,md不为空的时候,就一定会指向Max和Min当中的一个。

为了让求最大值的RadioButton在程序开始运行的时候就被选中,在Form的Load 事件中添加如下代码:private void Form1_Load(object sender, EventArgs e){= new MyDelegate );}运行的效果如下图所示:求最大值求最小值委托使用的注意事项在C#中,所有的委托都是从类派生的。

委托隐含具有sealed属性,即不能用来派生新的类型。

委托最大的作用就是为类的事件绑定事件处理程序。

在通过委托调用函数前,必须先检查委托是否为空(null),若非空,才能调用函数。

在委托实例中可以封装静态的方法也可以封装实例方法。

在创建委托实例时,需要传递将要映射的方法或其他委托实例以指明委托将要封装的函数原型(.NET中称为方法签名:signature)。

注意,如果映射的是静态方法,传递的参数应该是类名.方法名,如果映射的是实例方法,传递的参数应该是实例名.方法名。

只有当两个委托实例所映射的方法以及该方法所属的对象都相同时,才认为它们是想等的(从函数地址考虑)。

讨论委托类型从上面的案例中,我们可以发现,在使用委托之前,先要定义一个委托类型,如下所示:delegate int MyDelegate(int a, int b);MyDelegate md = null;既然叫做委托类型,就说明MyDelegate实际上是一个类,上面的写法只是一种简单的缩略写法,实际上,我们自己定义的委托,都是继承自类的,但是我们确不能自己定义一个类去继承自类,为了证明这点,我们可以使用ildasm工具,来查看“MyDelegate”的IL代码。

首先在Visual Studio控制台,如下图所示:在打开的“Visual Studio2008 Command Prompt”窗口中,输入ildam,如下图所示:运行之后,会出现ildasm的窗口,如下图所示:下一步,打开刚才编译好的exe程序,如下图所示:展开结点,并找到“MyDelegate”类型,将其展开,如下图所示:在上图中,我们可以看到,对“MyDelegate”,存在如下“说明”:extends [mscorlib]与此同时,还存在着四个方法,即:.ctor:构造方法BeginInvokeEndInvokeInvokeMulticastDelegate类MultiDelegate类是一个特殊类(Special Class),和类一样,该类只能够被编译器以及内置的工具类所继承,我们自定义的类是不能够显式的继承自该类的。

MultiDelegate类当中可以包括一个委托的链表,这个表中,可以包括一个或多个元素(每个元素都是一个委托),我们可以将这个表称为调用链。

当我们调用一个MultiDelegate的时候,位于该MultiDelegate调用链中的委托就会被串行调用。

这样我们就可以只调用一个方法,而多个相同签名的方法就会同时被串行调用。

关于多播委托的说明,我们会在后面的内容中进行讲解。

Invoke方法为了解释Invoke方法,我们先来回顾一下,当一个委托指向了一个方法时是如何调用的,代码如下所示:int c = (a,b);我们在调用委托,并执行该委托所指向的方法时,本质上就是调用了其Invoke 方法。

实际上,我们可以直接调用其Invoke方法,代码如下所示:int c = 另外,与Invoke方法对应的BeginInvoke,是对Invoke方法的一个异步调用,而EndInvoke是异步调用完成后的处理方法,关于异步调用的说明,我们将在多线程的章节中进行说明。

使用多播委托(MulticastDelegate)前面刚刚提及到MulticastDelegate,下面我们来看一下它的应用。

有的时候,我们想要调用一个委托,但同时可以执行多个方法(自定义事件中最为常见),比如,一个工作文档生成之后,系统要将生成文档日志,而且还要被保存到数据库中,对于以上二个操作,如果只想调用一个委托,就可以顺序完成,那么使用多播委托,就可以实现。

多播委托(MulticastDelegate)提供了一种类似于流水线式的钩子机制,只要加载到这条流水线上的委托,都会被顺序执行。

因为所有的委托都继承自MulticastDelegate,因此所的委托都具备多播特性。

下面能过一个控制台程序来说明多播委托(MulticastDelegate)的使用方式。

案例操作050602:使用多播委托首先,建立一个控制台程序。

在其中添加两个具备相同签名的方法:void CreateLogFile(string originalPath):用于创建日志文件void WriteToDb(string originalPath):用于将文件写入数据库代码如下:方法:void CreateLogFile(string originalPath)xists ("log")){("log");}StreamWriter sw = new StreamWriter("log/" ,true);("新文件已经创建,创建时间:{0},文件路径:{1}",DateTime .Now .ToLongTimeString(),originalPath );();("已经写入日志!");}方法:void WriteToDb(string originalPath)database=test;uid=sa;pwd=sa"); SqlCommand cmd = ();= "insert into tb_filesvalues(@ID,@FileName,@CreationTime,@FileBytes)";"@ID",.Value= ();"@r e a t i o n T i m e " , . V a l u e=; "@ Fl e N a m e " , . V a l u e =( o r i g i n a l Pth);"@FileBytes", ).Value=buffer ;();();();("已经写入数据库");}上面两个方法,具备相同签名,如果想同时串行调用这两个方法,还要定义一个委托类型,代码如下:xists ("log")){("log");}StreamWriter sw = new StreamWriter("log/" ,true);("新文件已经创建,创建时间:{0},文件路径:{1}",DateTime .Now .ToLongTimeString(),originalPath );();("已经写入日志!");}database=test;uid=sa;pwd=sa");SqlCommand cmd = ();= "insert into tb_filesvalues(@ID,@FileName,@CreationTime,@FileBytes)";"@ID",.Value= ();"@CreationTime",.Value=; " @ F ile N a m e " , .V a lu e =( o rginalPath);"@FileBytes", ).Value=buffer ;();();();("已经写入数据库");}自定义控件通常情况下,开发人员为了减少重复界面功能代码的编写量,往往会开发出一些用户控件或者是自定义控件。

相关主题