ProxyAttribute类型隐藏了实现一个上下文特性的许多复杂性,从本质上讲ProxyAttribute类型把IContextAttribute重新加工成两个简单的虚拟方法,只有其中一个需要插入我们的自定义代理:
Namespace System.Runtime.Remoting.Proxies{
Public class ProxyAttribute:Attribute,IcontextAttribute{
Public virtual MarshalByRefObject CreateInstance(Type t);
//为了清晰起见,余下部分省略
}
}
当一个newObj在一个应用了ProxyAttribure的类型上执行时,ProxyAttribute将调用它的CreateInstance虚拟方法。这个方法被期望返回一个当前类型的未初始化的(uninitialized)实例。注意这里的术语--未初始化的。由于这个过程是在激活阶段发生的,所以,对于重写的
Createlnstance方法返回的对象构造函数不能进行调用。更进一步地说,CLR将调用适合的构造函数,而小管Createlnstance方法返回的是什么。
ProxyAttribute.CreateInstance方法被标记为virtUal, 它的默认实现只是简单地分配一个请求类型的来初始化实例。然而,因为方法被标记为virtual,所以,我们现在有机会进入激活进程,而不用编写自己的上下文特性。为了插入我们的自定义特性,CreateInstance重写版本的实现看起来很像在原始MyCale上实现的工厂方法:
Using System;
Using System.Runtime.Remating.Proxies;
[AttributeUsage(AttributeTargets.Class)]
Public class PriorityProxyAttribute:ProxyAttribute{
ThreadPriority.eve;
Public PriorityProxyAttribute(ThreadPriority level)
{this.level=level;}
Public override
MarshalByRefObject CreateInstance(Type t){
//注意:我们委托我们的基类型,获得一个末初始化的实例!
MarshalByRefObject target=base.CreateInstance(t);
RealProxy pp=new PriorityProxy(target,t,level);
Return (MarshalByRefObject)pp.GetTransparentProxy();
}
}
注意Createlnstance方法的第一行.由于在一个newobj指令中间,因而不能使用new操作符或者其他创建新对象的实用部什,原因就是它们会触发对我们的CreateInstance方法的再一次调用(这将最终导堆栈溢出)。通过在基类型上调用Createlnstance方法,我们将得到目标对象类型的一个未初始化的实例,这正是我们需耍的。从技术角度讲,因为目标类型派生于ContextBoundObject,所以,我们实际上持有一个目标对象的透明代理,如图7.6所示
使用面向对面编程的读者在这一点上可能很关心目标对象.通过调用ProxyAttribute.CreateInstance,我们能够获得一个对象引用,而它的构造函数从没有执行过,对于你所关注的缘由,下面将得以证明。如果我们对这个对象做任何青意义的操作,其结果都将是未定义的。但我们所做的只是把该引用缓存到我们的自定义代理中——仅此而已,有幸的是,一旦我们从重写版本的Crearelnstance方法返回我们的自定义代理,CLR将进入激活的第二个阶段。在这个阶段,构造函数将被调用;但它将通过我们的自定义代理而被调用,这将给我们机机会(和责任)截获构造函数调用(constructor invocation)。
像其他许多方法调用一样,对于派生于ContextBoundObject类型的构造函数调用被表示为消息交换。实际上.用于构造函数调用的消息实现了一个额外的接口(IConstructionCallMessage.IConsrructionReturn Message).因此.你可以容易地侦测到该调用并不是另外一个方法调用。
实现处理构造函数调用的自定义代理有点复杂。其一 ,我们不能使用RemotingServices.ExecuteMessage转发调用。有幸的是,RedlProxy基类型提供了一个名为InitializeServerObject的方法为我们做这件事情.InitializeServerObject将返回一个响应消息,这是我们代理的Invoke方法实际上所能返回的;不过,这个消息包含了未经包装的对象引
用。为保证创建者取得透明代理的引用,我们需要构造一个新的响应消息,它包含我们的自定义代理,而不是我们截获调用的原始对象。理论上,我们可以创建一个新的ReturnMessage,使其包含我们的对象引用。但遗憾的是,我们无法这样做。我们必须使用EnterpriseServicesHelper.CreateConstructionReturnMessage静态方法。
为了使我们的Invoke例程合适地处理构造调用(construction call),下两的代码展示了所需要的修改。注意,处理构造调用的所有特别情形都发生在方法的第二步:
值得一提的是,只有自定义代理实现者需要编写这个代码。当所有的代码都就位后,使用代码将变得和应用特性一样简单;
[PriorityProxy(ThreadPriority.Highest)]
Public class MyCalc3:ContextBoundObject,Icalculator{
Public double Add(double x,double y){return x+y ;}
Public double Multiply(double x,double y){return x*y}
}
自定义代理的开发人员为了让代理工作,必须仔细检查一些有趣的环节,然而,代理的使用者则拥有一个十分简单的使用模型。现在不再需要稀奇古怪的工厂方法。而只是简单地在MyCalc3上调用new,就能够触发刚才描述的所有代码。