您的位置:首页 > 编程语言 > C#

C#委托,事件理解入门 (译稿)

2009-11-22 20:50 381 查看
目录

l 导论

l 什么是委托

l 事件的理解

l 事件 关键字

l 最后

导论

在学习C#中的委托和事件过程中,我读了许多文章来理解他们二者究竟是怎么一回事,以及如何使用他们,现在我将整个的理解过程陈述以下,我学到的每一方面,恐怕也是你们需要掌握的 :-)。

什么是委托?

委托和事件这两个概念是完全配合的。委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。

每一个委托都有自己的签名,例如:Delegate int SomeDelegate(string s, bool b);是一个委托申明,在这里,提及的签名,就是说SomeDelegate 这个委托 有 string 和 bool 类型的形参,返回一个int 类型。

上面提及的:当你对委托实例化时,要提供一个引用函数,将其作为它构造函数的参数。这里要注意了:被引用的这个函数必须和委托有相同的签名。

看下面的函数:

private int SomeFunction(string str, bool bln){...}

你可以把这个函数传给SomeDelegate的构造函数,因为他们有相似的签名(in other words,他们都有相同的形参类型和个数,并且返回相同的数据类型)。

SomeDelegate sd = new SomeDelegate(SomeFunction);

sd 引用了 SomeFunction,也就是说,SomeFunction已被sd所登记注册,如果你调用 sd,SomeFunction 这个函数也会被调用,记住:我所说 SomeFunction的含义,后面,我们会用到它。

现在,你应该知道如何使用委托了,让我们继续理解事件之旅……

事件的理解

我们知道,在C#中:

l 按钮(Button)就是一个类,当我们单击它时,就触发一次click事件。

l 时钟(Timer)也是一个类,每过一毫秒,就触发一次tick事件。

让我们通过一个例子来学习,假定有这样的情节:

现在有一个Counter的类,它有一个方法 CountTo(int countTo, int reachableNum),该方法表示:在指定的时间段内(0~~countTo),当到达指定的时间点reachableNum时,就触发一次NumberReached事件。

它还有一个事件:NumberReached,事件是委托类型的变量。意思是:如果给事件命名,用event关键字和要使用的委托类型申明它即可,如下所示:

public event NumberReachedEventHandler NumberReached;

在上面的申明中,NumberReachedEventHandle 仅是一个委托,更确切的表示应该是:NumberReachedDelegate。但是微软从不这样认为MouseDelegate或者PaintDelegate,,而是称谓:MouseEventHandler 或者 PaintEventHandler。所以

NumberReachedEventHandler 比NumberReachedDelegate听起来更方便一些,OK?好了,让我们继续,现在你知道了,在我们声明事件之前,需要象下面这样的形式来定义委托:

public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);

现在声明的委托 NumberReachedEventHandle,它有一个void 返回值,和object,NumberReachedEventArgs两个形参。就像我们在第一节中强调的那样,当实例化委托时,作为实参传入的函数也必须拥有和委托同样的签名。

在你的代码中, 你是否用过PaintEventArgs 或者 MouseEventArgs来确定鼠标的移动位置?是否在触发Paint事件的对象中用过Graphics 属性?实际上,为用户提供数据的类都是继承于System.EventArgs类,就是我们常说的事件参数类,如果事件不提供参数,就不定义该类。在我们的例子中,我们通过下面的类提供预期的时间点。

public class NumberReachedEventArgs : EventArgs

{

private int _reached;

public NumberReachedEventArgs(int num)

{

this._reached = num;

}

public int ReachedNumber

{

get

{

return _reached;

}

}

}

好,有了前面的介绍,让我们到Counter类里面看看:

namespace Events

{

public delegate void NumberReachedEventHandler(object sender,

NumberReachedEventArgs e);

/// <summary>

/// Summary description for Counter.

/// </summary>

public class Counter

{

public event NumberReachedEventHandler NumberReached;

public Counter()

{

//

// TODO: Add constructor logic here

//

}

public void CountTo(int countTo, int reachableNum)

{

if(countTo < reachableNum)

throw new ArgumentException(

"reachableNum should be less than countTo");

for(int ctr=0;ctr<=countTo;ctr++)

{

if(ctr == reachableNum)

{

NumberReachedEventArgs e = new NumberReachedEventArgs(

reachableNum);

OnNumberReached(e);

return;//don't count any more

}

}

}

protected virtual void OnNumberReached(NumberReachedEventArgs e)

{

if(NumberReached != null)

{

NumberReached(this, e);//Raise the event

}

}

}

在Counter中,如果到达指定的时间点,就触发一次事件,有以下几个方面需要注意:

l 通过调用NumberReached(它是NumberReachedEventHandler委托的实例)来完成一次触发事件。

NumberReached(this, e); 通过这种方式,可以调用所有的注册函数。

l 通过 NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum); 为所有的注册函数提供事件数据。

l 看了上面的代码,你可能要问了:为什么我们直接用 OnNumberReached(NumberReachedEventArgs e)方法来调用NumberReached(this,e),而不用下面的代码呢?

if(ctr == reachableNum)

{

NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);

//OnNumberReached(e);

if(NumberReached != null)

{

NumberReached(this, e);//Raise the event

}

return;//don't count any more

}

这个问题问得很好,那就让我们再看一下OnNumberReached 签名:

protected virtual void OnNumberReached(NumberReachedEventArgs e)

①你也明白 关键字protected限定了 只有从该类继承的类才能调用该类中的所有方法。

②关键字 virtual 表明了 在继承类中可以重写该方法。

这两点非常有用,假设你在写一个从Counter继承而来的类,通过重写OnNumberReached 方法,你可以在事件触发之前,进行一次其他的工作。

protected override void OnNumberReached(NumberReachedEventArgs e)

{

//Do additional work

base.OnNumberReached(e);

}

注意:如果你没有调用base.OnNumberReached(e), 那么从不会触发这个事件!在你继承该类而想剔出它的一些其他事件时,使用该方式是非常有用的。

l 还要注意到:委托 NumberReachedEventHandler 是在类定义的外部,命名空间内定义的,对所有类来说是可见的。

好,该我们来实际操作使用Counter类了。

在我们简单的应用程序中,我们有两个文本框,分别是:txtCountTo和txtReachable:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Events
using System;

namespace Events

0
0

(请您对文章做出评价)

« 上一篇:一网打尽 .Net 开发过程中安装、调试的常见问题与错误!
» 下一篇:如何向access表中的日期列插入日期数据 (C#实现)

posted @ 2005-10-16 13:43 Kevin Li 阅读(27821) 评论(52) 编辑 收藏 网摘


发表评论

回复 引用 #1楼 210.22.118.* 2005-11-04 14:26 | xumanjun[未注册用户]
不错,加油。

回复 引用 #2楼 221.233.24.* 2005-11-09 22:26 | jimfire[未注册用户]
果然是好东西,谢谢。

回复 引用 #3楼 60.209.248.* 2006-02-16 11:28 | sanson_1980[未注册用户]
谢谢 写的太好了 通俗易懂, 例子也不错

回复 引用 #4楼 60.209.248.* 2006-02-16 14:18 | sanson_1980[未注册用户]
能不能 具体说说 用这个的好处在哪儿

回复 引用 查看 #5楼[楼主]2006-02-17 23:14 | finesite
to sanson_1980:

委托的一个特征就是它不用知道也无需关心它引用对象的类,任何对象都可以,关键的是对象所调用方法的描述类型和引用类型要与委托所要求的相匹配(即各方法的签名要相同).

我的理解有点类似不管白猫黑猫,抓住老鼠都是好猫的意思,并且还要 要求这些好猫(不管黑白胖瘦)逮老鼠时要用两只前爪同时向前扑这一动作,不当之处,共通交流.

回复 引用 #6楼 218.1.114.* 2006-02-23 09:21 | microsy[未注册用户]
您好,想问一个问题,那就是:如果说委托是函数的抽象的话,相当于C,C++中的函数指针的话?那事件的本质是什么呢?
我能理解委托和委托的实例,但是对事件是什么有些模糊?
在激活事件的时候,发现它有点像函数,在为它挂接处理的时候,感觉又有点像函数指针的数组?谢谢!

回复 引用 #7楼 61.129.178.* 2006-04-05 10:23 | xiaoluo[未注册用户]
看了你的文章,我一下子就明白了!
谢谢你!

回复 引用 #8楼 220.178.24.* 2006-08-19 22:06 | 帅哥[未注册用户]
太好了 !!!!写得很不错~~~~~~~~~~~~

回复 引用 查看 #9楼2006-10-05 10:57 | 壮志


回复 引用 查看 #10楼2006-12-19 10:15 | Kevin Wu
嗯,不错的文章。。译得也挺好

回复 引用 #11楼 221.234.251.* 2007-01-19 16:10 | youfriend[未注册用户]
好长的网业!

回复 引用 #12楼 58.211.245.* 2007-02-27 17:53 | flyingfish[未注册用户]
看了评论就知道你写的不错
我看了好长时间的委托也绕不出那个迷魂阵
希望通过你的文章
顺理渡我过去
嘿嘿

回复 引用 #13楼 58.211.245.* 2007-02-28 10:05 | flyingfish[未注册用户]
拜读了你的文章,受益匪浅!!^_^

回复 引用 查看 #14楼2007-03-21 22:04 | KOP
thx:)

回复 引用 查看 #15楼[楼主]2007-05-05 12:26 | finesite
谢谢上面的回复,我继续努力

回复 引用 #16楼 222.130.196.* 2007-05-08 16:01 | wqerq[未注册用户]
感觉没什么难的啊,是不能我还没看懂?

回复 引用 #17楼 218.94.69.* 2007-06-18 11:28 | andy[未注册用户]
不错,一看就懂了

回复 引用 #18楼 60.176.242.* 2007-06-20 11:11 | 杨[未注册用户]
真的不错,太好了总算可以多加理解一点

回复 引用 #19楼 218.106.60.* 2007-09-19 11:04 | IESer[未注册用户]
--引用--------------------------------------------------
microsy: 您好,想问一个问题,那就是:如果说委托是函数的抽象的话,相当于C,C++中的函数指针的话?那事件的本质是什么呢?
我能理解委托和委托的实例,但是对事件是什么有些模糊?
在激活事件的时候,发现它有点像函数,在为它挂接处理的时候,感觉又有点像函数指针的数组?谢谢!
--------------------------------------------------------
文中不是说了吗?
委托是一个类,事件是委托类型的变量。

委托类似于函数指针,但在C/C++中的函数指针只能引用静态方法,而委托既能引用静态方法,也能引用实例方法。

那么作为委托实例的事件,相当于一个函数指针变量,在C/C++中,函数指针变量就是回调函数

抛砖引玉,不知道有没有说错

回复 引用 #20楼 117.9.61.* 2007-11-25 16:54 | wxqaz[未注册用户]
呵呵,先谢楼主啦,拷下来研究先!

回复 引用 查看 #21楼[楼主]2007-11-29 23:54 | finesite
谢谢楼上的各位,有帮助就好,我继续努力!!

回复 引用 #22楼 220.170.32.* 2007-12-07 11:22 | dymmyming[未注册用户]
谢谢楼主哟。。呵呵,终于理顺了这个弯弯绕

回复 引用 #23楼 211.136.253.* 2008-04-03 08:43 | hezechang[未注册用户]
不错..

回复 引用 #24楼 123.191.21.* 2008-04-21 10:57 | oudaojiang[未注册用户]
相当不错,注释得当,引人思考,这对我这样的初学者老说真是太好了,
谢谢楼主。

回复 引用 #25楼 58.213.133.* 2008-05-30 09:19 | 10752p[未注册用户]
还好!

回复 引用 #26楼 222.38.148.* 2008-06-05 12:01 | Q[未注册用户]
:(,怎么我还是不是很懂委托用法!55555555

回复 引用 #27楼 60.30.94.* 2008-06-17 14:23 | 草原[未注册用户]
巴错,这篇文章挺好滴,挺值得学习,不过初学者估计要领悟几天~~~~~~

回复 引用 #28楼 59.40.183.* 2008-06-22 14:02 | 万凯[未注册用户]
我用了几天时间才懂了一点点~

回复 引用 #29楼 61.175.132.* 2008-07-14 12:14 | 清风拂柳[未注册用户]
看了你的文章,受益菲浅,谢谢!

回复 引用 #30楼 61.50.131.* 2008-07-14 13:32 | birdonekk[未注册用户]
受益匪浅,thank you very much !

回复 引用 查看 #31楼[楼主]2008-07-15 12:53 | Kevin Li
没想到这么多留言,谢谢大家的留言,我会继续努力的!

回复 引用 #32楼 117.36.4.* 2008-07-22 19:00 | saner[未注册用户]
不错,加深了理解。
支持下~~~~~~~~~

回复 引用 #33楼 218.3.80.* 2008-07-27 17:33 | ysen[未注册用户]
首先谢谢楼主的无私的奉献!!!
通过文章我的理解如下
首先委托是一个类 因为只有类可以实例化一个对象!
二就是这个类的构造函数需要参数,但是它的参数很特别,
实例化的对象通过+=获取不只一个的方法对象,方法对象与定义的委托
的方法内参数必须是一致的(这里面的委托返回值在实际应用中只能是void,如果它有返回值的话那么委托的存在就没有意义了,比如说委托+=多个的时候它的方法参数有好多个,那么它们有多个返回值那么委托的返回值应该被指定给谁)!
三就是委托的类应该是个指针数组或者是个指针容器(C#里有的容器可以不用初始化容器的大小)可以存储多个实例方法,
四就是委托可以调用不同类的方法,这样我想起列遍循环的功能非常强大!
比起C++的函数指针强大很多,它不仅可以调用静态的函数还可以调用动态的函数!

回复 引用 #34楼 58.218.80.* 2008-08-09 23:29 | zp[未注册用户]
多谢楼主的指点 让我学到不少

回复 引用 #35楼 203.110.162.* 2008-08-26 11:33 | rxj[未注册用户]
谢谢楼主,继续学习

回复 引用 #36楼 124.160.42.* 2009-01-19 17:48 | MF[未注册用户]
顶,好文章

回复 引用 #37楼 58.248.161.* 2009-02-19 00:26 | 不错[未注册用户]
写得不错啊。

回复 引用 #38楼 123.112.117.* 2009-03-31 12:09 | sd[未注册用户]
esd

回复 引用 #39楼 123.112.117.* 2009-03-31 12:20 | 举个例子[未注册用户]
假如有四种动物,分别是老虎,小鹿,老鼠,蛇。现在小鹿逃跑了,这就是一个”事件“,我们称之为“逃跑事件”,对这个事件的处理当然是“追”个事件处理方法,但是有谁去执行”追“这个事件处理呢,当然是老虎,为什么是老虎不是其他动物呢,这是因为自然法则,那么自然法则就是一个"委托"。自然法则并没有把它委托给其他三种动物,同样老鼠跑是要蛇去追的,加入没有这个自然法则,上述行为是不会产生的,狮子要是在场的话,老虎吃饱了不饿,也可以委托给狮子,让狮子去追。
不知道这个例子恰不恰当,欢迎交流:binbin_wu@msn.com

回复 引用 查看 #40楼2009-04-14 11:53 | uxspy
--引用--------------------------------------------------
举个例子: 假如有四种动物,分别是老虎,小鹿,老鼠,蛇。现在小鹿逃跑了,这就是一个”事件“,我们称之为“逃跑事件”,对这个事件的处理当然是“追”个事件处理方法,但是有谁去执行”追“这个事件处理呢,当然是老虎,为什么是老虎不是其他动物呢,这是因为自然法则,那么自然法则就是一个"委托"。自然法则并没有把它委托给其他三种动物,同样老鼠跑是要蛇去追的,加入没有这个自然法则,上述行为是不会产生的,狮子要是在场的话,老虎吃饱了不饿,也可以委托给狮子,让狮子去追。
不知道这个例子恰不恰当,欢迎交流:binbin_wu@msn.com
--------------------------------------------------------
这个例子,,,更容易理解~~~

逃跑事件:OnNumberReached:Counter
自然法则:NumberReachedEventHandle
(老虎/狮子)追的具体事件处理:oCounter_NumberReached ,oCounter_NumberReached2 <---(可以蹦着追,也可以大喊,再跑开枪了,具体看怎么实现)

因此:
自然法则(说):(如果)逃跑事件发生,那么我拜托 oCounter_NumberReached;oCounter_NumberReached2(去追)
---------------------
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);
---------------------
OnNumberReached <---逃跑
---------------------
NumberReached(this,e) <---老虎/狮子(准备了,准备追!!)
---------------------

不知道这样解释和不合理,欢迎交流uxspy.bj@gmail.com

回复 引用 查看 #41楼2009-04-14 15:37 | 易水长
@uxspy
哈哈,解释得不错!支持好文!

回复 引用 #42楼 222.178.150.* 2009-04-14 17:53 | xiongmao
终于明白 了!谢了!

回复 引用 查看 #43楼2009-07-22 19:08 | 子逸
我顶你, 楼主

回复 引用 #44楼 221.221.157.* 2009-07-31 11:26 | lyx_wq[未注册用户]
自己理解:
委托就是一个 函数的容器 可以通过+=,-=放入或去掉函数;
当事件发生,容器里有什么函数就调用什么函数,也不知道理解的对不?

回复 引用 查看 #45楼2009-09-03 11:25 | 青林一霸
看了上面的代码,你可能要问了:为什么我们直接用 OnNumberReached(NumberReachedEventArgs e)方法来调用NumberReached(this,e),而不用下面的代码呢?

讲的不错,我终于似乎明白了一些
但以上这一点我还是没看明白

回复 引用 查看 #46楼2009-09-06 01:39 | 好俊的功夫啊
就是说为了实现多态和复有,,
回答楼上

回复 引用 查看 #47楼2009-09-17 10:41 | 2w213
@lyx_wq
我也是这么理解的。

回复 引用 #48楼 124.128.158.* 2009-09-29 09:50 | ww2521[未注册用户]
太好了!!谢谢前辈的无私指导!!!!

回复 引用 查看 #49楼2009-10-10 10:53 | 喝过酒的猫
例子很精典,谢了

回复 引用 查看 #50楼2009-10-12 15:07 | hellocsdn
关键是事例模型用的不是很好。所以有些人还是没理解。就算理解了也没消化。

回复 引用 #51楼 60.191.126.* 2009-10-12 15:25 | 121212[未注册用户]
讲得比<<visual c#2005技术内幕>>要清楚易懂.

刷新评论列表 刷新页面 返回页首
发表评论 程序员招聘 求职 IT新闻
昵称:

主页:

评论内容:











注销 订阅回复

[使用Ctrl+Enter键快速提交]

IT新闻:
· Chrome OS安全性分析
· 盛大:湖南广电改革的药引
· 谷歌宣布年底前正式关闭GrandCentral网站
· 一个简单的 Google Chrome OS FAQ
· 谷歌赴京谈判版权门:不赞同赔偿措辞

招聘信息:
· 人人网招聘研发工程师
· 杭州雷鸟计算机软件有限公司招聘.NET 软件开发工程师
· 广州浩讯信息技术有限公司招聘软件开发工程师
· 北京世纪摇篮网络技术有限公司招聘.NET程序员
· 北京搜房科技发展有限公司北京招聘网页设计
· 北京赛优科技有限公司招聘PB程序员
· 上海众大新技术发展有限公司招聘.NET程序员

微软/劳动部双认证免费.NET培训
博客园个人主页上线测试
Windows 7专题
NHibernate专题

China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
China-Pub 计算机绝版图书按需印刷服务

链接:切换模板
导航:网站首页 个人主页 社区 IT新闻 博问 闪存 网摘 招聘 找找看 Google搜索

在知识库中查看:
C#委托,事件理解入门 (译稿)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: