欢迎来到.net学习网

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

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

本教程章节列表

对象和上下文(一)

创建时间:2013年03月12日 21:38  阅读次数:(3766)
分享到:
当你构建一个截获管道时.一个特别讨厌的问题可能发生,那就是需要防止一个对象的非截获引用泄漏到外部世界,这将给调用方提供绕过由截获器提供服务的机会。考虑下面的简单类:
public class Bob:MarshalByRefObject{
 public void f(){}
 public Bob GetIt(){
  return this; //危险
}
}

在这个例子中,GetIt方法返回目标对象的引用。如果透明代理在调用方和这个对象之间,那么客户端现在将有两个方法引用这个对象:一种是直接引用,另一种是通过代理间接地引用。现在考虑下面的客户端代码:
static void UseIt(Bob proxy){
 Bob b=proxy.GetIt();
 proxy.f(); //通过代理引用
 b.f(); //绕过代理
}

如果这个代理将执行某种关键服务(例如,一致性管理或者安全控制)那么,该服务将在第二个对Bob.f的调用上被绕过去。

一些基于截获的系统需要程序员使用系统提供的实用部件显式地保护this引用,为此,MTS提供了SafeRef API, Enterprise Java Beans (EJB)则提供了EJBContext.getEJBEome方法。在这两种情形下,例程都要求开发人员显式地管理代理,因而使得整个透明代理截获的观念失效了。

为了避免这些问题,CLR提供了一个架构,你可以通过它以简单的方式使用对象,CLR则保证执行合适的截获代码。这个架构是基于每个对象与某个上下文(context)的关联,该上下文表示了与该对象相关联的所需服务。

每个CLR应用程序被分成一个或多个上下文。上下文自身是对象,它们是System.Runtime.Remoting.Contexts.Context类型的实例。与ContextBoundObject兼容的对象在创建时被绑定到一个特定的上下文上,这些对象被作为上下主绑定的(context-bound)实例而引用。而其他对象都被认为是上下文灵活的(context-agile),并且不属于任何特定的上下文(参见图7.9)。上下文灵活的对象将忽略上下文架构,并且在概念上忽略应用程序中的所有上下文。
上下文绑定的对象与上下文灵活的对象

线程可以随意地进入和离开上下文。System.Runtime.Thread.CurrentContext是一个静态的每线程(per-thread)属性,它返回一个上下文的引用,而当前线程正是在这个上下文中执行的。当每个应用程序开始时,一个上下文就会被分配给它,并且线程将在这个上下文中开始执行。这个初始上下文被称为默认上下文(default context),你可以通过Context.DefaultContext静态属性访问它。

为了演示上下文灵活的和上下文绑定的对象之间的差别,考虑下面的两个类型:
public class Bound:ContextBoundObject()
{
 Context lastContext=Thread.CurrentContext;
 pulbic void f(){
  Context here=Thread.CurrentContext;
//这将总是成立的
 Debug.Assert(here==lastContext);
 this.lastContext=here;
}
}

public class Agile{
Context lastContext=Thread.CurrentContext;
public void f(){
 Context here=Thread.CurrentContext;
//这将可能或者不可能成立!
 Debug.Assert(here==lastContext);
 this.lastContext=here;
}
}

CLR不保证Agile.f方法将在哪个上下文中执行,原因就是Agile不是上下文绑定的类型。相比之下,CLR保证执行Bound.f方法的线程将总是在同一个上下文中运行---特别地,对象在创建时被绑定到这个上下文。

CLR通过确保所有上下文绑定的对象引用实际上都引用一个代理,从而保证这一点。要得到一个上下文绑定对象的直接引用是不可能的。这意味着你可以作出下面的断言:
public class Bound:ContextBoundObject{
 public void(){
  Debug.Assert(RemotingServices.IsTransparentProxy(this));
}
}

正如这里所展示的,甚至连this引用都不是这个对象的直接引用,这便解决了我们早先提出的问题,也就是确保没有任何原始的(raw)引用泄漏到客户端。因为即便是这个对象自己也不能获得原始的引用,所以绕过截获层是不可能的。就效率来说,假如调用线程已经在正确的上下文中,那么,用于上下文绑定对象的标准代理只是简单地将调用传达给目标对象。否则,代理将遍历前一节描述的标准消息处理链。为了确保合适的上下文对象总是用于目标对象,在创建目标对象时,CLR将在接受链中插入一个消息接收器(CrossContextChannel类型的)。消息接收器将调用线程的上下文切换到目标对象的上下文,然后,在调用被分发后,再切换回调用方的上下文。

下面的伪代码演示了CrossContextChannel是如何工作的:
namespace System.Runtime.Remoting.Channels{
public class CrossContextChannel:ImessageSink{
  ImessageSink nextSink;
  Context targetContext;
  public IMessage SyncProcessMessage(Imessage request){
//第一步:预处理调用
 Context old=Thread.CurrentContext;
 Thread.CurrentContext=targetContext;
//第二步:转发调用到下一个接收器
 Imessage resp=nextSink.SyncProcessMessage(request);
//第三步:后处理调用
 Thread.CurrentContext=old;
//第四步:将消息返回到上游的接收器
 return resp;
}
}
}

这个伪代码与实际的实现相比做了大量的简化,读者可以使用ILDASM.EXE查看mscorlib.dll中的真实代码。

像调用上下文那样,上下文允许任意数据被关联到命名插槽的属性包,不同的是,关联到上下文的数据与对象是并存于那个上下文中的,而不管给定方法来自何处。下面的代码演示了一个上下文作为属性包的用法:
public clas Bound:ContextBoundObject{
 LocalDataStoreSlot slot=null;

public void SetIt(int n)
{
 if(slot==null)
  slot=Thread.CurrentContext.AllocateDataSlot() ;
 Thread.CurrentContext.SetData(slot,n);
}

public int GetIt(){
 if(slot==null)
  return 0
 else
  return (int)Thread.CurrentContext.GetData(slot);
}
}

注意,不像调用上下文那样,调用上下文是用简单的基于文本的名字作为键,而GetData和SetData方法使用LocalDataStoreSlot。

除了刚才描述的简单关联数组,上下文有零个或多个命名的上下文属性(Context property)。上下文属性充当上下文的逻辑字段,并且像字段一样,在上下文的生存期内,上下文属性的集合不会改变,上下文属性并不是任意数据,它们是实现了System.RunTime.Remoting.Contexts.IContext.Property接口的对象,每个上下文属性都有一个唯一基于文本的名字,并且,你可以在一个特定的上下文中通过调用Context.GetProperty方法访问它。另外,你还可以使用Context.ContextProperties得到一个上下文的上下文属性的所有引用,它返回上下文属性的一个数组。R鷁癳剉^\'`<
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

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

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

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

最新评论

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