用代表来简化.NET类

ZDNet软件频道 时间:2002-09-27 作者:BUILDER.COM |  我要评论()
本文关键词:
本文讲述了什么是代表(delegate),并举例说明了代表在.net开发中的应用。
设想你有两个名字分别为Caller和Notifier的类,并且你的设计规定类Notifier为类Caller完成某项工作并当工作完成后通知类Caller(通过一个名为Notify的方法(method))。这种情况在编程中很常见,尤其是当某一个类生成一个新的异步线程让它为之工作时。开发者可以在.NET用户接口 (UI)库中用这种方法向自己的代码通知用户接口事件。

让我们看看以下三种可以解决上述问题的方法。为了简明起见,我们不考虑例子中的线程的建立——假定它们已经建立好了。

基于数据成员(reference)的解决方案

解决不同类之间通信的最简单的方法就是就是给类Notifier添加一个对类Caller的引用,这样,当Notifier完成指定的任务后就可以调用Caller.Notify(来建立通信)。请看清单A中用C#写的例子。

你会看出类Notifier的构造函数有一个Caller类型的参数,构造函数将该参数存在类的私有数据成员owner中,owner是Caller类型的,它存储了Caller类型的一个实例。一旦类Caller调用函数Notifier.DoSomething,类Notifier执行任务(在本例中是往控制台写上一些字符),然后调用owner的Notify方法。

拥有一个私有数据成员是类间通信的最简单同时比较有效的方法。但是它存在一个问题,即相互通信的两个类之间的关联太紧密了(紧耦合)。如果一个与Caller无关的类想调用Notifier,该怎么办呢?

基于接口的解决方案

让类Caller有一个接口是一种更好的方法。在清单B中你会看到类Caller和Notifier已经被重写并增加了NotifyMe接口。NotifyMe只有一个名为Notify的方法。在本方案中,Caller执行NotifyMe并借助于后者的构造函数发一个参数(转为NotifyMe类型)给Notifier。当Notifier完成任务(在函数DoSomething中指定),它就开始调用NotifyMe.Notify,并由类Caller执行被调用的方法。

基于接口的解决方案自然要比前面那种方案要好。相互通信的两个类之间不需要很多互相了解,而且符合上述接口的类都可以从被调用的类(worker class)中收到消息。然而,假定你要处理的两个类没有公共接口,或是调用类(caller class)需要用静态方式获得通知,那该怎么办呢?还有更糟糕的情况,如果你需要通知多个方法、甚至多个类,那个怎么处理?

了解“代表(delegate)”

如果你恰好是C++开发者,看到上述问题,你也许会回答“用函数指针”。对没有经验的人来说,函数指针就是指向函数入口地址的指针。问题是,在运行时,函数入口地址可能被废弃并且可能在被调用函数运行时调用者根本就不知道被调用函数的名字。函数指针是功能强大的结构,但是它有一下两个问题:

  • 从总体上来说,函数指针并不是具有类型安全(type safe),也就是说,不能在运行时检测到诸如参数类型不匹配、丢失参数或返回值类型不对等错误。
  • 在.NET这样内存被管理的系统(managed memory envionment)中,为了得到最佳性能,线程在内存中的地址是变动的。你在几分钟前获得的有效地址,对应的内存块可能现在移到另一个地方了,现在也许你的指针正指向花生酱黄油三明治上了(*^_^*)。

.NET框架提供一种功能类似于传统的函数指针的安全数据结构:系统代表(system Delegate)。在C#中,该类型与关键字delegate对应,这样你就可以像下面那样说明和调用一个新的代表:
//说明一个代表类型的变量
public delegate void SomeDelegate();
//建立SomeDelegate类型的实例
SomeDelegate d = new SomeDelegate(ClassMethod);
//调用代表函数
d();


百度大联盟认证黄金会员Copyright© 1997- CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号 京ICP备09041801号-159
京公网安备:1101082134