Application Domain可以看作是一个Assembly的逻辑容器在程序执行过程中如果遇到需要的Type并没有定义在已经加载的Assemblies中 CLR会把相应的Assembly加载的该Application Domain中每个Application Domain都有一个属于自己的加载器堆(Loader Heap)用于维护从Application Domain创建以后所用到的所有的Type以及这些Type对应的方法表——维护这样一个Mapping定义在Type中的所有方法和经过JIT编译后x代码(只考虑bit处理器) Application Domain之间是相互隔离互不干扰在一个Application Domain创建的对象不能被另一个Application Domain直接调用反映在内存分配上面——就是各个Application Domain使用各个独立的内存地址空间一个对象根据他所对应的类型(如SystemMarshalByRefObject通过传递引用的方式)或者属性(比如对于定义了System SerializableAttribute的Type采用传递值得方式)以两种不同的方式在Application Domain之间传递——By Reference 和By Value 这些都是地球人都知道的NET的基本原理但是相信很多人没有尝试过通过Coding的方式证明这种机制 那么现在我们就先来看看我们的Sample 下面是运行结果的Screen Shot 接下来我们来分析这段代码 我们首先定义个个不同的Type他们都一个相同的方法——GetAppDomain()用于获取执行该方法的真正的Application Domain GeneralType一般的Type 没有什么特别 MarshalByValueType定义了一个System SerializableAttribute(你也可以通过使它实现 SystemRuntimeSerializationISerializable Interface来模拟这个Sample)它将以By Value的方式在不同的Application Domain之间传递 MarshalByRefType继承自System MarshalByRefObject该类型的对象它将以By Reference的方式在不同的Application Domain之间传递 public class GeneralType { AppDomain GetAppDomain() { return AppDomainCurrentDomain; } } [Serializable] public class MarshalByValueType { public AppDomain GetAppDomain() { return AppDomainCurrentDomain; } } public class MarshalByRefType : MarshalByRefObject { public AppDomain GetAppDomain() { return AppDomainCurrentDomain; } } 在Main()中我们首先创建一个新的Application Domain并为他指定一个Friendly Name——newAppDomain AppDomain appDomain = AppDomainCreateDomain(newAppDomain) 接着我们分别在这个新建的Appliation Domain中创建我们在中定义的个类型的对象——generalObjectmarshalByValueObject marshalByRefObject并试着把它传递到当前的Application Domain——Default Application Domain从运行的结果我们可以看出当我们传递generalObject的时候一个Exception被抛出从Error Message可以开出原因——Type ArtechAppDomainIsolationGeneralType in assembly ArtechAppDomainIsolation Version= Culture=neutral PublicKeyToken=null is not marked as serializable从而我们可以看出一般的Type是不能在不同的Application之间传递的 通过证明了标记的System SerializableAttribute属性和继承自System MarshalByRefObject的Type对应的对象是可以在不同的Application Domain之间传递呢但是他们之间又会有怎样的差异呢?他们是真正的对象呢?还仅仅是位于新建Application Domain中的对象一个代理(Proxy)?我们通过调用定义在SystemRuntimeRemoting中的静态方法 IsTransparentProxy了解Remoting的人相信对这个方法不会感到陌生他用于判断某个对象是否是一个Transparent Proxy(在当我们跨Application Domain远程调用一个Remote Object的时候实际上我们并非直接调用Remote Object的方法而是通过一个同Client处在同一个Application Domain的Transparent Proxy对象间接地调用远程对象——Transparent Proxy具有一个Remote Object的Reference可以轻易地找到这个Remote Object其实在真正的场景中Client调用Transparent ProxyTransparent Proxy再去调用Real ProxyReal Proxy最终才去调用Remote Object——如果你想进一步地了解Remoting你可以参照MSDN) 通过运行结果我们可以看到MarshalByValueType对象marshalByValueObject IsTransparentProxy方法返回False而对于MarshalByRefType对象marshalByRefObject则返回 True这就充分证明了标记了System SerializableAttribute属性的Type所对应的对象是一个真正意义上的对象而对于继承自MarshalByRefObject Type当该Type对应的对象从一个Application Domain传递到另一个Application Domain后另一个Application Domain获得的仅仅是原来对象的Proxy而已 我们可以从传递的机制来解释这种差异当你把System SerializableAttribute属性运用要某个Type或者让某个Class实现 SystemRuntimeSerializationISerializable Interface的时候你实际上是给该Type赋予了一种能力——一种可以序列化成XML的能力(XMLSerializer负责把对象序列化成 XML)当这种对象从一个Application Domain传递到另一个Application Domain的时候Object先被序列化成XML接着把XML传递到另一个Application Domain中在新的Application Domain中通过反序列化重新生成一个新的Object——这个新的Object和处于另一个Application Domain已经没有任何关系 而对于而对于继承自MarshalByRefObject Type的对象虽然他不能够序列化成XML但是可以通过传递Reference的方式在Application Domain之间传递当这种传递实质上是通过在另一个Application创建一个拥有该对象引用的一个Proxy而这个Proxy依赖一这个处理另一个 Application Domain的真正对象 上面我们实际上已经说清楚了两个对象传递的差异——By Value 和By Reference现在我们来进一步验证前面我们说的我们继续来看看我们的代码 我们通过调用连个对象的GetAppDomain()方法从而获得真正执行该方法的Application Domain然后再和当前的Application Domain进行比较我们发现对于MarshalByValueType对象marshalByValueObject真正执行操作是在当前的 Application Domain中进行的而对于MarshalByRefType对象marshalByRefObject则是我们新建立的Application Domain这充分证明了当marshalByValueObject传递到新的Application后生成一个和原来对象一模一样的对象这个对象具有执行自身操作的能力而对于marshalByRefObject由于它只是一个Proxy而已他只有把对对象的操作请求发送给真正的位于另一个Application Domain的真正对象(同时他也负责把远程对象的执行结果返回给调用者)真正的操作实际上是发生在远程对象的Application Domain 由于marshalByRefObject依赖于远程对象所以当我们卸载掉Host远程对象的Application Domain对marshalByRefObject的任何调用将变得无效所以这里可以对下面这段代码作出解释 注在分布式开发中我们会大量接触到By Value 和By Refernce传递一般而言By Value用于商业实体的传递(Business Entity)而By Reference用于远程调用(RPC)或者是调用Service |