代理是对函数进行包装的对象.docx
代理是对函数进行包装的对象在C+/C1.I中,代理是对函数进行包装的对象;而事务是一种为客户程序供应通知的类机制。在前几篇文章中,已经多次演示了假如让一个句柄在不同的时间,被引用至不同的对象,从而以更抽象的方法来解决程序中的问题,但是,也能运用代理通过函数来达到同样的效果;代理是包装r函数的一个对象,且而实例函数而言,也能通过特定的实例,与这些函数发生联系。一旦一个代理包装r-个或多个函数,你就能通过代理来调用这些函数,而无须事先了解包装了哪些函数。请看例1中的代码,在标号1中,定义一个代理类型De1.,由于运用了上下文关键字de1.egate,所以有点像函数的声明,但与函数声明不同的是,此处声明的是一个代理类型De1.的实例,其可包装进随意接受一个int类型作为参数并返回一个ini值类型的函数(随意有效的参数列衣及返回类型组合都是允许的)。一旦定义了某种代理类型,它只能被用于包装具有同样类型的函数;代理类型可被定义在源文件中或命名空间的范围内,也能定义在类中,并可有pub1.ic或private访问限制属性。例1:usingnamespaceSystem;refstructA(staticintSquare(inti)returni*i;refstructBintCube(inti)(returni*i*i;/*1*/de1.egateintDe1.(intva1.ue);intmain()相同的参数类型及返回类型,因此它们能被包装进同类型的代理中。留意,即使两个函数均为pub1.ic,当考虑它们与De1.的赖容性时,它们的可访问性也是不相关的,这样的函数也能被定义在相同或不同的类中,主要由程序员来选择。一旦定义了某种代理类型,就可创建此类型实例的句柄,并进行初始化或赋值操作,如标号2中所示的静态函数A-Square,及标号5中所示的实例函数BiiCubeo(此处只是出于演示的目的,否则把Cube做成实例函数没有任何好处。)创建一个代理实例涉及到调用一个构造函数,假如是在包装一个静态函数,只需传递进一个指向成员函数的指针:而对实例函数而言,必需传递两个参数:一个实例的句柄及指向实例成员函数的指针。在初始化代理实例之后,就能间接地调用它们包装的函数了,用法与干脆调用原函数一样,只不过现在用的是代理实例名,如标号3与6,由包装函数返回的他也是像干脆调用函数时那样获得。假如一个代理实例的值为11u1.1.ptr,此时再试图调用被包装的函数,会导致System:INu1.1.ReferenceException类型异样。以下是输出:d(10)resu1.t=100d(10)resu1.t=1000传递与返回代理有时,把包装好的函数传递给另一个函数,会特别有用,接受一方的函数并不知道会传递过来哪个函数,并且它也无须关切,只需简洁地通过包装好的代理,间接调用此函数就行了。下面以集合中元素排序来说明,reCase在标号6中,创建了一个Compare代理类型的实例,用它来包装StrCOmPare:=CompareIgnoreCase,并把此代理句柄传递给Sort函数,其将会利用比较函数进一步进行处理。正如大家所看到的,Sort可接受一个代理类型的参数一而此参数可像其他函数参数一样,可为传值、传址、传引用。在标号7中,调用了FindCompariSonMethod函数,其返回一个De1.代理类型,接着在标号7及8中调用了包装过的函数。此处要重点说一下标号8:首先,FindCompariSonMethod函数是被调用来获得代理实例一其常用于调用底层函数:其次,这两个函数的调用操作符都有同等的优先级,所以它们从左至右调用。FindCompariSonMethod函数中也用了一些逻辑用于确定究竟须要包装哪个函数,此处就未作具体说明白。代理类型的赖容性一个代理类型只与它自身相兼容,与其他任何代理类型都不兼容,即使其他类型的包装函数均为同一类型。请看例3,特别明显,代理类型D1.与函数A:M1与A:M2兼容,代理类型D2也与这些函数兼容,然而,这两个代理类型在标号5、6、8、9中并不能互换运用。例3:de1.egatevoidD1();de1.egatevoid)2();pub1.icstructstaticvoidM1.O*.*/sta兼容/*5*/d1.=d2;不兼容/*6*/d2=d1.;/不兼容/*7*/X(d1.);兼容/*8*/X(d2);不兼容*9*/Y(d1.);不兼容/*10*/Y(d2):兼容代理类型的合并一个代理实例事实上能包装多个函数,在这种状况下,被包装的函数集被维护在一个调用列表中,当合并两个代理实例时,它们的调用列表也以指定的依次连接起来,并产生一个新的列表,而现有的两个列表并没有发生变更。当从调用列表中移除一个或多个函数时,也会产生一个新的列表,且原始列表不会发生变更。请看例4中的代码,每个函数调用后的输出都写在相应函数后。例4:usingnamespaceSystem:de1.egatevoidD(intx);refstructActionsstaticvoidF1.(inti)Conso1.e:Write1.ine(Actions:F1:0,i);StatF1、F2,t-F3Actions:F1:35Actions:F2:35instanceofActions:F3:35/*7*/cd3-=cd4:移除t-F3cd3(40);调用FKF2/*8*/cd3-=cd1.;/移除F1.cd3(45):调用F2/*9*/cd3-=cd2;移除F2,调用列表现在为空*10*Conso1.e:Write1.ine(cd3=0,(cd3=nu1.1.ptr?nu1.1.:notnu1.1.);Actions:F1:40Actions:F2:40Actions:F2:45cd3=nu1.1.代理可通过+和+=操作符来合并,如标号3、4中所示。两个单入口列表会连接成一个新的双入口列表,以先左操作数,后右操作数的依次。新的列表被cd3引用,而现有的两个列表并未变更。在此要留意的是,不能合并不同类型的代理。正如在标号4中所见,同一个函数可在一个调用列表中包装多次:而在标号5中,也说明向一个调用列表能同时包含类与实例函数。代理可通过-或-=操作符移除,如标号6中所示。当同一个函数在调用列表中出现多次时,一个对它的移除恳求会导致最右边的项被移除。在标号6中,这产生了一个新的三入口列表,其被cd3引用,且前一个列表保持不变(因为从前被cd3引用的列表现在引用计数为零,所以会被垃圾回收)。当一个调用列表中的最终一项被移除时,代理将为nu1.1.ptr值,此处没有空调用列表的概念,因为,根本就没有列表了。例5中演示了另一个代理合并与移除的例子,正如标号3a与3b中所示,两个多入口调用列表是以先左操作数,后右操作数的依次连接的。假如想移除一个多入口列表,只有当此列表为整个列表中严格连续的子集时,操作才会胜利。例如,在标号4b中,你可以移除F1.和F2,因为它们是相邻的,对标号5b中的两个F2及标号6b中的F1.、F2来说,道理也是一样的。但是,在标号7b中,列表中有两个连续的F1.,所以操作失败,而结果列表则是最起先的列表,它包含有4个入口。例5:usingnamespaceSystem:de1.egatevoidD(intx);voidF1.(i11ti)Conso1.e:Write1.ine(F1.:0,i);voidF2(inti)Conso*3b*/cd3=Iist1.+1.ist2;/F1.+F2+F2+F1.cd3(20);/*4a*/cd3=Iist1.+1.ist2;/F1.+F2+F2+F1./*4b*/cd3-=cd1.+cd2:/F2+F1.cd3(30);/*5a*/cd3=Iist1.+1.ist2:/F1.+F2+F2÷F1./*5b*/cd3-=cd2÷cd2;/F1.+F1.cd3(40);/*6a*/cd3=Iist1.+1.ist2;/F1.+F2+F2+F1./*6b*/cd3-=cd2+cd1.;/F1.+F2cd3(50);/*7a*/cd3=Iist1.+1.ist2;/F1.+F2+F2+F1./*7b*/cd3-=cd1.+cd1.;/F1.+F2+F2+F1.cd3(60);System:De1.egate代理类型的定义,会隐式地创建一个对应的类(CIaSS)类型,并且全部的代理类型均从类库System:De1.egate继承而来。要定义一个这样的类,唯一的方法就是通过de1.egate上下文关键字。代理类为隐式的Sea1.ed,因此它们不能被用作基类。另外,一个非代理类也不能从System:!De1.egate继承。例6演示了几个De1.egate函数的用法:例6:usingnamespaceSystem:de1.在标号4中,创建了一个四入口的调用列表。倘如传递进来的调用列表不为空,Process1.ist函数将调用在列表中除了特定对象以外的全部函数,例如,在标号5a中,没有解除任何入口,因此全部的函数都会被调用:在标号5b中,t1.被解除在外,而标号5c中,与对象12有关的两个入口都被解除了,结果输出如下:Objectt1.:100Objectt2:100Objectt3:100Object1.2:12:6关于函数Process1.ist,假如参数中的代理实例为nu1.1.ptr,即没有调用列表,那它将干脆返回;假如解除的对象为nu11ptr,那么列表中全部的函数都将被调用:假如存在要解除的对象,就要像标号8中那样把调用列表当作代理数组取出,接着,在标号9中逐个排查不相符的入口,最终,在标号10中调用余下的这些函数。尽管在调用列表中每个入口都是De1.类型,但GetInvocation1.ist返回一个基类De1.egate数组,所以在调用每个代理实例之前,需像标号10那样先转换成类型Do事务在C÷÷C1.I中,事务是一种当某种重要事情发生时,为客户程序供应通知的机制。鼠标单击就是事务的一个典型例子,在事务发生之前,有关的客户程序必需先注册它们感爱好的事务,如,当检测到鼠标单击时,这些程序就会接到通知。通过添加或删除一个或多个感爱好的事务,事务列表可在运行时增长或缩减,请看例7Server类型的定义,在标号1中,Server类定义了代理类型MewMsgEven1.Hand1.er(一般约定在用于事务处理时,代理类型添加EventHand1.er的后缀名),接着,在标号2中,定义了一个名为ProcessNewMsg的公共事务(event在此为一个上下文关键字)。一个事务必需有一个代理类型,事实上,像这样的一个事务已经是一个代理实例了,而且因为它被默认初始化为nu1.1.ptr,所以它没有调用列表。例7:uMsg(msg):):当通过一条消息调用时,函数Broadcast将调用包装在ProcessNewMsg调用列表中全部的函数。CIient类定义在例8中,一个C1.ient的类型实例无论何时被创建,它都会通过向为Server:ProcessNewMsg维护的代理列表中添加一个实例函数(它关联到实例变量),来注册它所感爱好的新Server消息,而这是通过+=操作符来完成,如标号5中所示。只要这个入口一是主程序,一起先,没有注册任何函数,所以当发送第一个消息时,不会获得任何通知。然而,一旦构造了c1.,通知列表就包含了此对象的一个入口,而接下来c2与c3的构造使这个列表