2 委托技术与设计模式
委托技术是.NET引入的一种重要技术,使用委托可以实现对象行为的动态绑定,从而提高设计的灵活性。
2.1 .NET中的委托技术
.NET运行库支持称为“委托”的引用类型,其作用类似于C++中的函数指针。与函数指针不同,委托实例独立于其封装方法的类,主要是那些方法与委托类型 兼容。另外,函数指针只能引用静态函数,而委托可以引用静态和实例方法。委托主要用于.NET Framework中的事件处理程序和回调函数。
所有委托都从System.Delegate继承而来并且有一个调用列表,这是在调用委托时所执行方法的一个链接列表。产生的委托可以用匹配的签名引用任何方法,没有为具有返回类型并在调用列表中包含多个方法的委托定义返回值。
可以使用的委托Cimbine及Remove方法在其调用列表中添加和移除方法。若要调用委托,可使用Invoke方法,或者使用BeginInvoke和EndInvoke方法异步调用委托。委托类的实现由运行库提供,而不由用户代码提供。
委托适用于那种在某些语言中需要用函数指针来解决的情况,但是与函数指针不同,它是面向对象和类型安全的。
委托声明定义一个类,它是从System.Delegate类派生的类。委托实例封装了一个调用列表,其中列出了一个或多个方法,每个方法称为一个可调用 实体。对于实例方法,可调用实体由一个实例和该实例的方法组成;对于静态方法,可调用实体仅由一个方法组成。如果用一组合适的参数来调用一个委托实例,则 该委托实例所封装的每个可调用实体都会被调用,并且使用上述同一组参数。
委托实例的一个有用的属性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的类型兼容。即只要方法的返回类型和参数表是相同的,则方法与委托类型兼容,方法的名称不一定要与委托类相同。
定义和使用委托分为声明、实例化和调用3个步骤。委托用委托声明语法声明,如:
delegate void myDelegate( );
声明一个名为myDelegate的委托,它不带参数并且不返回任何结果,如:
class Test
{
static void F( )
{
System.Console.WriteLine (“Test.F”);
}
static void Main ( )
{
myeDelegate d = new myDelegate (F);
d ( );
}
}
创建一个myDelegate实例,然后立即调用它。这样做并没有太大的意义,因为直接调用方法会更简单。当涉及其匿名特性时,委托才能真正显示出其效果,如:
void MultiCall (myDelegate d, int count ) {
for (int I = 0; I < count; I++) {
d( );
}
}
显示一个重复调用 myDelegate的MultiCall 方法,这个方法不知道,也不必知道myDelegate的目标方法的类型、该方法具有的可访问性或者是否为静态。对它来说最重要的是目标方法与myDelegate兼容。
2.2示例
下面的例子说明了委托的实现,代码如下:
2 namespace DelegateExample
3 {
4 public class TemplateMethod
5 {
6 public delegate float Comp(float a,float b);
7 public Comp myComp;
8 public TemplateMethod()
9 {}
10 public float DoComp(float[] f)
11 {
12 float nf = float.NaN;
13 foreach(float df in f)
14 {
15 if(float.IsNaN(nf))
16 nf = df;
17 else
18 nf = myComp(nf,df);
19 }
20 return nf;
21 }
22
23 }
24}
2.3 委托技术与GOF设计模式中委托的关系
需要指出的是,.NET中的委托技术与GOF在《设计模式》中所提列的委托的意图一致,但在实现方法上有相当大的区别。.NET中的委托更进一步地降低了对象间的耦合性,将静态的组合关系变为运行时的动态组合关系。
GOF在《设计模式》中定义的委托是:“委托是一种组合方法,它使组合具有与继承同样的复用能力。在委托方式下,有两个对象参与处理一个请求,接受请求的 对象将操作委托给它的代理者(delegate),它类似于子类将请求交给它的父类处理。使用继承时,被继承的操作总能引用接受请求的对象。在C++中通 过this成员变量,在Smalltalk中则通过self。委托方式为了得到同样的效果,接受请求的对象将自身传给被委托者(代理人),使被委托的操作 可以引用接受请求的对象。”
如果采用.NET的委托技术,上述结构可以更加灵活。Window不引用Rectangle即可实现Area的计算,为此首先声明一个计算面积的委托定义,示例代码如下:
public delegate float Darea();
然而在Window类中声明与这个代理一致的接口:
class Window
{
public Darea Area;
}
这里不需要引用Rectangle类,只是在执行时动态绑定即可:
Rectangle rc = new Rectangle();
Window w = new Window();
w.Area = new Darea(rc.Area);
这样当调用w的Area时,实际调用的是Reactangel的Area方法。从实现意图上看,.NET的委托更好地实现了GOF所阐述的意图,结构上也 更为灵活。但这两种委托解决的不是一个层面的问题,GOF的委托强调的是一种策略,而.NET和委托技术则是具体实现。
2.4 委托技术与设计模式实现
采用委托技术可以进一步实现用组合代替继承的思路,很多采用继承实现的关系可以采用委托实现。采用委托可以简化下列设计模式的使用。
(1)模板方法:这种方法采用继承实现具体方法,采用委托可以动态实现方法的组合。
(2)观察者:可以使用事件委托实现观察者与主题之间的通信。
(3)中介者:使用委托可以去除工件与中介者之间的耦合关系。