欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表
最新评论

.net本质论-代理的类型

创建时间:2013年02月26日 22:29  阅读次数:(5352)
分享到:

代理类型

前面一节以对RemotingServices.ExecuteMessage的讨论作为结束。细心的读者可能已经注意到,还有一个新的类型没有作出解释。这个类型便是System.MarshalByRefObject。

用于说明代理的首选类型就是接口,这并不是想当然的。接口有一个特征使得它们非常适合代理:接口号类不一样,它总是隐含着虚方法的分发。正是由于这个原因,JIT编译器决不会通过一个基于接口的引用内联方法调用。而类却不是这样的。

为了理解通过类使用透明代理的相关问题,考虑下面的类定义
Public sealed class Bob{
int x;
public void DoIt(int n){x+=n ;}
}

JIT编译器很可能内联对Bob.Dolt的调用,并且没有方法调用将实际发生。也就是说,无论Bob.DoIt的调用点在哪里,JIT编译器只是简单地插入用于自增Bob.x字段的代码。这种内联的扩展机制跳过了透明代理对于截获调用的尝试。因此,假如你将typeOf(Bob)传递给RealProxy构造函数,那么将会抛出一个异常,原因就是对于通过Bot类型的引用产生的所有调用,代理的基础架构无法保证截获它们。

对于接口,这根本不是问题,因为JIT编译器决不会内联基于接口的方法调用。然而,在很多情况下,允许类用于透明代理是根有用的。下面讨论System.MarshalByRefObject。

System.MarshalByRefObject类型其实是一个误称。它的基本功能是取消JIT编译器的内联,这样,透明代理就能够施展它的魔法。当JIT编译器内联一个方法时,它首先查看这个方法所属类型是否派生于System. MarshalByRefObject。如果是,就不进行内联。并且,任何对该类型实例字段的访问都将通过System.Object上的两个不知名方法(FieldGetter和FieldSetter方法)进行,这使得透明代理公开了类的公有字段,并且对这些字段的访问还通知给了透明代理。

派生于System.MarshalByRefObject的类的实例可能有(或者没有)与之相关联的代理。特别是存一个方法体内的this引用决不是一个代理,准确地说,它总是对象的原始引用。代理可能总是需要访问对象。为了实现这个目标,你可以从System.ContextBoundObject派生。

System.ContextBoundobject的方法派生于System.MarshalByRefObject,它通知运行时总是在对象的前面插入—个透明代理:例如,考虑下面的三个类:
Using System;
Public class Bob{}
Public class Steve:MarshalByRefObject{}
Public class George:ContextBoundObject{}

根据MarshalByRefObject和ContextBoundObject昀描述,你可以作出下面的断言:
Using System.Runtime.Remoting:
Public sealed class Utils{
Public static void Proof(Bob b,Steve s,George g){
//g总是一个TP
Debug.Assert(RemotingServices.IsTransparentProxy(g));
//b不是一个TP
Debug.Assert(!RemotingServices.IsTransparentProxy(b));
//s可能是也可能不是一个TP
Bool whoKnows=RemotingServices.ISTransparentProxy(s);
}
}

Ceorge派生于ContextBoundObject,因此类型George的所有非空引用一定指向一个透明代理(甚至包括this引用!)。由于Bob没有MarshalByRefObject派生,类型Bob的引用决不指向一个代理,并且,由于这个原因,JIT编译器可以通过类型Bob的引用选择内联其方法。相比之下,Steve派生于MarshalByRefObject,因此,类型Steve的引用可能指向一个透明代理。这便阻止了JIT编译器通过Steve类型的引用内联方法调用。
    
对于一个可代理的(proxiable)类,它必须派生于MarshalByRefObject,根据这一点,你可以很容易地写出下面的无操作(no-op)代理:
Public class NoOpProxy:RealProxy{
Readonly MarshalByRefObject target:
Public NoOpProxy(MarshalByRefObject target):base(target.GetyType())
{
This.target=target;
}
Public override Imessage Invoke(Imessage request){
 ImethodClassMessage call=(IMethodCallMessage)request;
 Return RemotingServices.ExecuteMessage(target,call);
}
}

这个代理什么也没做,它只是位于透明代理和目标对象之间,并通过堆栈生成器接收器转发所有的方法调用。

支持截获的整个目的通常是为了以类型独立的(type-independent)方式,对方法凋用进行预处理和后处理。在本章的剩余部分,我们将展示一个使用截获器的示例,它提高了在调用目标方法之前的线程优先级,井在将控制权返回给调用方之前还原这个优先级。下面的代理实现通过使用到目前为止本章所描述的各种机制实现了上述功能:
Public class PriorityProxy:RealProxy{
Readonly MarshalByRefObject target;
Readonly ThreadPriority level;
Public PriorityProxy(marshalByRefObject target,Type type,ThreadPriority Level):base(type)
{
This.target=target;
This.level=level;
}
Public override Imessage Invoke(Imessage request){
ImethodCallMessage call=(IMethodCallMessage)request;
//第1步:调整优先级
Thread here=Thread.CurrentThread;
ThreadPriority old=here.Priority;
Here.Priority=level;
//第2步:转发调用
Imesage response=RemotingServices.ExecuteMessage(target,call);
//第3步:恢复为旧的优先级
Here.Priority=old;
//第4步:将响应消息返回给TP
Return response;
}
}

现在我们只需要一个工厂方法透明地插入截获器:
Public class MyCalc:MarshalByRefObject,Icalculator{
Public static MyCalc Create(ThreadPriority level){
MyCalc target=new MyCalc();
PriorityProxy rp=new PriorityProxy(target,typeof(MyCalc),level);
Return (MyCalc)rp.GetTransparentProxy();
}
Private MyCalc(){}
Public double Add(double x,double y){return x+y ;}
Public double Multiply(double x,double y){return x*y ;}
}

注意,工厂方法(create)在客户端和新创造的目标对象之间插入了这个代理。MyCale方法不必处理线程优先级,并且客户端只需要使用这个工厂方法创建对象。通过把构造函数设置为private,就可以保证工厂方法的使用。

前面的例子通过透明截获提供了线程优先级的调整,这样,就从客户端和目标类中删除了线程优先级设置代码,然而这个截获器的插入并不是完全透明的。更进一步地说,类的实现者不得不写一个特别的工厂方法,并且,客户端必须使用这个工厂方法。这样的结果导致类的实现者需望多做一些工作,并且,这对于客户端来说是很别扭的应用模型。当然,如果开发人员允许客户端对默认构造函数的访问,那么新的对象将不会拥有截获器的好处。

如果我们的目标类型派生于ContextBoundObject,而不是MarshByRefObject,那么,我们就可以利用处理方式的差别,即CLR对于派生于ContextBoundOoject的类型,在处理newobj请求时,与通常(无上下文绑定)的类型是不同的。当CLR遇到一个newobj CIL操作码,而所要创建的新对象属于由ContextBoundObject派生的类型,这时,CLR将会很巧妙地处理,允许在该对象的实例化中加入第三方的扩展,而不仅仅分配内存。整个过程被称为对象激活(object activation)。对象激活允许我们透明地注入自定义代理,而客户端只需要在他们选择的语言中很自然的使用new关键字。

在为新的上下文绑定对象分配内存前,CLR将查看应用到已被实例化的目标类型上的自定义特性。CLR将特别查找实现System.Runtime.Remoting.Contexts.IContextAttribute接口的自定义特性,CLR给与这些特别的特性[通常称为上下文特性(context Attributes)]处理newobj
请求的机会:然而,因为newobj只在派生于ContextBoundObject的类型上操作时才查找上下文特性,所以,将上下文特性应用到一个非ContextBoundObject类型上没有意义。上下文特性的全面讨论将在本章后面进行。现在,我们将重点关注预定义的上下文特性System.Runtime.Remoting.Proxies.ProxyAttrlbute。
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 3+10=? 的结果(结果是:13)
结  果: