没有合适的资源?快使用搜索试试~ 我知道了~
C#中的委托和事件(续).pdf
4星 · 超过85%的资源 需积分: 9 11 下载量 87 浏览量
2008-11-21
10:15:56
上传
评论
收藏 179KB PDF 举报
温馨提示
试读
32页
如果你看过了 “C#中的委托和事件.pdf” 一文,我想你对委托和事件已经有了一个基本的认识。但那些远不是委托和事件的全部内容,还有很多的地方没有涉及。本文将讨论委托和事件一些更为细节的问题,包括一些大家常问到的问题,以及事件访问器、异常处理、超时处理和异步方法调用等内容。
资源推荐
资源详情
资源评论
C#中的委托和事件(续)
引言
如果你看过了 C#中的委托和事件 一文,我想你对委托和事件已经有了一个基本的认
识。但那些远不是委托和事件的全部内容,还有很多的地方没有涉及。本文将讨论委托和事
件一些更为细节的问题,包括一些大家常问到的问题,以及事件访问器、异常处理、超时处
理和异步方法调用等内容。
为什么要使用事件而不是委托变量?
在 C#中的委托和事件 中,我提出了两个为什么在类型中使用事件向外部提供方法注
册,而不是直接使用委托变量的原因。主要是从封装性和易用性上去考虑,但是还漏掉了一
点,事件应该由事件发布者触发,而不应该由客户端(客户程序)来触发。这句话是什么
意思呢?请看下面的范例:
NOTE:注意这里术语的变化,当我们单独谈论事件,我们说发布者(publisher)、订阅
者(subscriber)、客户端(client)。当我们讨论 Observer 模式,我们说主题(subject)和观
察者(observer)。客户端通常是包含 Main()方法的 Program 类。
class Program {
static void Main(string[] args) {
Publishser pub = new Publishser();
Subscriber sub = new Subscriber();
pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged);
pub.DoSomething(); // 应该通过 DoSomething()来触发事件
pub.NumberChanged(100); // 但可以被这样直接调用,对委托变量的不恰当使
用
}
}
// 定义委托
public delegate void NumberChangedEventHandler(int count);
// 定义事件发布者
public class Publishser {
private int count;
public NumberChangedEventHandler NumberChanged; // 声明委托变量
//public event NumberChangedEventHandler NumberChanged; // 声明一个事件
public void DoSomething() {
// 在这里完成一些工作 ...
if (NumberChanged != null) { // 触发事件
count++;
NumberChanged(count);
}
}
}
// 定义事件订阅者
public class Subscriber {
public void OnNumberChanged(int count) {
Console.WriteLine("Subscriber notified: count = {0}", count);
}
}
上面代码定义了一个 NumberChangedEventHandler 委托,然后我们创建了事件的发布
者 Publisher 和订阅者 Subscriber。当使用委托变量时,客户端可以直接通过委托变量触
发事件,也就是直接调用 pub.NumberChanged(100),这将会影响到所有注册了该委托的订
阅者。而事件的本意应该为在事件发布者在其本身的某个行为中触发,比如说在方法
DoSomething()中满足某个条件后触发。通过添加 event 关键字来发布事件,事件发布者的
封装性会更好,事件仅仅是供其他类型订阅,而客户端不能直接触发事件(语句
pub.NumberChanged(100)无法通过编译),事件只能在事件发布者 Publisher 类的内部触
发(比如在方法 pub.DoSomething()中),换言之,就是 NumberChanged(100)语句只能在
Publisher 内部被调用。
大家可以尝试一下,将委托变量的声明那行代码注释掉,然后取消下面事件声明的注
释。此时程序是无法编译的,当你使用了 event 关键字之后,直接在客户端触发事件这种行
为,也就是直接调用 pub.NumberChanged(100),是被禁止的。事件只能通过调用
DoSomething()来触发。这样才是事件的本意,事件发布者的封装才会更好。
就好像如果我们要定义一个数字类型,我们会使用 int 而不是使用 object 一样,给予
对象过多的能力并不见得是一件好事,应该是越合适越好。尽管直接使用委托变量通常不会
有什么问题,但它给了客户端不应具有的能力,而使用事件,可以限制这一能力,更精确地
对类型进行封装。
NOTE:这里还有一个约定俗称的规定,就是订阅事件的方法的命名,通常为“On 事件
名”,比如这里的 OnNumberChanged。
为什么委托定义的返回值通常都为 void?
尽管并非必需,但是我们发现很多的委托定义返回值都为 void,为什么呢?这是因为
委托变量可以供多个订阅者注册,如果定义了返回值,那么多个订阅者的方法都会向发布者
返回数值,结果就是后面一个返回的方法值将前面的返回值覆盖掉了,因此,实际上只能获
得最后一个方法调用的返回值。可以运行下面的代码测试一下。除此以外,发布者和订阅者
是松耦合的,发布者根本不关心谁订阅了它的事件、为什么要订阅,更别说订阅者的返回值
了,所以返回订阅者的方法返回值大多数情况下根本没有必要。
class Program {
static void Main(string[] args) {
Publishser pub = new Publishser();
Subscriber1 sub1 = new Subscriber1();
Subscriber2 sub2 = new Subscriber2();
Subscriber3 sub3 = new Subscriber3();
pub.NumberChanged += new GeneralEventHandler(sub1.OnNumberChanged);
pub.NumberChanged += new GeneralEventHandler(sub2.OnNumberChanged);
pub.NumberChanged += new GeneralEventHandler(sub3.OnNumberChanged);
pub.DoSomething(); // 触发事件
}
}
// 定义委托
public delegate string GeneralEventHandler();
// 定义事件发布者
public class Publishser {
public event GeneralEventHandler NumberChanged; // 声明一个事件
public void DoSomething() {
if (NumberChanged != null) { // 触发事件
string rtn = NumberChanged();
Console.WriteLine(rtn); // 打印返回的字符串,输出为 Subscriber3
}
}
}
// 定义事件订阅者
public class Subscriber1 {
public string OnNumberChanged() {
return "Subscriber1";
}
}
public class Subscriber2 { /* 略,与上类似,返回 Subscriber2*/ }
public class Subscriber3 { /* 略,与上类似,返回 Subscriber3*/ }
如果运行这段代码,得到的输出是 Subscriber3,可以看到,只得到了最后一个注册方
法的返回值。
如何让事件只允许一个客户订阅?
少数情况下,比如像上面,为了避免发生“值覆盖”的情况(更多是在异步调用方法
时,后面会讨论),我们可能想限制只允许一个客户端注册。此时怎么做呢?我们可以向下
面这样,将事件声明为 private 的,然后提供两个方法来进行注册和取消注册:
// 定义事件发布者
public class Publishser {
private event GeneralEventHandler NumberChanged; // 声明一个私有事件
// 注册事件
public void Register(GeneralEventHandler method) {
NumberChanged = method;
}
// 取消注册
public void UnRegister(GeneralEventHandler method) {
NumberChanged -= method;
}
public void DoSomething() {
// 做某些其余的事情
if (NumberChanged != null) { // 触发事件
string rtn = NumberChanged();
Console.WriteLine("Return: {0}", rtn); // 打印返回的字符串,输出
剩余31页未读,继续阅读
资源评论
- 普通网友2011-11-08很好,挺清晰。谢谢分享
hnzzzsl
- 粉丝: 1
- 资源: 15
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功