一、概述

使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll),这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。处理asp.net所涉及的类大多数定义在System.Web程序集中。

  1. 当exe程序集加载完毕,.Net会在当前进程中创建一个默认应用程序域,这个应用程序域的名称与程序集名称相同。默认应用程序域不能被卸载,并且与其所在的进程同生共灭。
  2. 应用程序域只是允许它所加载的程序集访问由.Net Runtime所提供的服务。这些服务包括托管堆(Managed Heap),垃圾回收器(Garbage collector),JIT 编译器等.Net底层机制.
  3. 一个应用程序域中如果出现了致命错误导致崩溃,只会影响其本身,而不会影响到其他应用程序域。实现了错误隔离。

应用程序域是进程中承载程序集的逻辑分区,在应用程序域中存在更细粒度的用于承载.NET对象的实体,即.NET上下文Context。

所有的.NET对象都存在于.NET上下文当中,每个AppDomain当中至少存在一个默认上下文(context0),:

优点

托管程序为什么要使用应用程序域呢?概括其优点如下:

  1. 在一个应用程序中出现的错误不会影响其他应用程序。
  2. 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。
  3. 能够在不停止整个进程的情况下停止单个应用程序。
  4. 使用应用程序域时,可以卸载在单个应用程序中运行的代码。
  5. 在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。

属性

  • ActivationContext           获取当前应用程序域的激活上下文。
  • ApplicationIdentity           获得应用程序域中的应用程序标识。
  • BaseDirectory  获取基目录,它由程序集冲突解决程序用来探测程序集。
  • CurrentDomain  获取当前 Thread 的当前应用程序域。
  • FriendlyName              获取此应用程序域的友好名称。
  • Id  获得一个整数,该整数唯一标识进程中的应用程序域。
  • SetupInformation     获取此实例的应用程序域配置信息。

方法

  • CreateDomain(String)  使用指定的名称新建应用程序域。
  • CreateInstance(String, String)   创建在指定程序集中定义的指定类型的新实例。
  • CreateInstanceAndUnwrap(String, String) 创建指定类型的新实例。 形参指定定义类型的程序集以及类型的名称。
  • CreateInstanceFrom(String, String)   创建在指定程序集文件中定义的指定类型的新实例。
  • DoCallBack           在另一个应用程序域中执行代码,该应用程序域由指定的委托标识。
  • ExecuteAssembly(String)   执行指定文件中包含的程序集。
  • ExecuteAssemblyByName(String)     在给定其显示名称的情况下执行程序集。
  • GetAssemblies      获取已加载到此应用程序域的执行上下文中的程序集。
  • GetData         为指定名称获取存储在当前应用程序域中的值。
  • IsDefaultAppDomain   返回一个值,指示应用程序域是否是进程的默认应用程序域。
  • Load(AssemblyName)  在给定 AssemblyName 的情况下加载 Assembly。
  • SetData(String, Object)  为指定的应用程序域属性分配指定值。
  • Unload           卸载指定的应用程序域。

程序集

程序集是包含一个或多个类型定义文件和资源文件的集合。它允许我们分离可重用类型的逻辑表示和物理表示。程序集可以是静态的或动态的。静态程序集可以包括 .NET Framework 类型(接口和类),以及该程序集的资源(位图、JPEG 文件、资源文件等)。静态程序集存储在磁盘上的可移植可执行 (PE) 文件中。您还可以使用 .NET Framework 来创建动态程序集,动态程序集直接从内存运行并且在执行前不存储到磁盘上。您可以在执行动态程序集后将它们保存在磁盘上。

全局程序集缓存:安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存。 全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集。

二、基本操作

在.Net 中,将应用程序域封装为了AppDomain类,这个类提供了应用程序域的各种操作,包含 加载程序集、创建对象、创建应用程序域 等。通常的编程情况下下,我们几乎从不需要对AppDomain进行操作。

1、获取当前运行的代码所在的应用程序域。

可以使用AppDomain类的静态属性CurrentDoamin,获取当前代码所在的应用程序域;或者使用Thread类的静态方法GetDomain(),得到当前线程所在的应用程序域:

AppDomain currentDomain = AppDomain.CurrentDomain;
AppDomain currentDomain = Thread.GetDomain();

NOTE:一个线程可以访问进程中所包含的所有应用程序域,因为虽然应用程序域是彼此隔离的,但是它们共享一个托管堆(Managed Heap)。

2、获取应用程序域的名称。

使用AppDomain的实例只读属性,FriendlyName:

string name = AppDomain.CurrentDomain.FriendlyName;

3、从当前应用程序域中创建新应用程序域。

可以使用CreateDomain()静态方法,并传入一个字符串,作为新应用程序域的名称(亦即设置FriendlyName属性):

AppDomain newDomain = AppDomain.CreateDomain("New Domain");

4、配置应用程序域(在创建前):

AppDomainSetup domainInfo = new AppDomainSetup();
domainInfo.ApplicationBase = "e:\\AttributeTest";
domainInfo.ConfigurationFile = "e:\\AttributeTest\\1.exe.config";
AppDomain domain = AppDomain.CreateDomain("MyDomain", null, domainInfo);

5、判断是否为默认应用程序域:

newDomain.IsDefaultAppDomain()

6、卸载应用程序集

AppDomain.Unload(newDomain);

三、在新的应用程序域中执行程序集

加载可执行程序:

AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
newAppDomain.ExecuteAssembly("Example.exe");//或使用ExecuteAssemblyByName
AppDomain.Unload(newAppDomain);

四、在新的应用程序域中创建对象

将程序集加载到应用程序域中的方式:

  • a.Assembly.Load(assemblyName);使用程序集名来加载到应用程序域中,为常用方法。
  • b.Assembly.LoadFrom():加载给定其文件位置的程序集,通过此方法加载程序集将使用不同的加载上下文。
  • c.Assembly.ReflectionOnlyLoad和 Assembly.ReflectionOnlyLoadFrom:将程序集加载到仅反射上下文中,可检查(但不能执行)加载到该上下文中的程序集,同时允许检查以其他平台为目标的程序集。
  • e.Type.GetType()加载程序集
  • f.AppDomain的Load方法加载程序集。主要用于实现COM互操作性,不应该使用该方法将程序集加载到除从其调用该方法的应用程序域以外的其他应用程序域。
  • d.AppDomain的CreateInstance和CreateInstanceAndUnwrap方法。

具体见反射基础:https://www.cnblogs.com/springsnow/p/9433924.html

在新的应用程序域中创建对象,可以使用AppDomain的实例方法CreateInstanceAndUnWrap()或者CreateInstance()方法。方法包含两个参数,第一个参数为类型所在的程序集,第二个参数为类型全称(这两个方法后面会详述):

DemoClass obj = (DemoClass)newDomain.CreateInstanceAndUnWrap("ClassLib", "ClassLib.DemoClass");
//或者
ObjectHandle objHandle = newDomain.CreateInstance("ClassLib", "ClassLib.DemoClass");
DemoClass obj = (DemoClass)objHandle.UnWrap();
//或者
DemoClass obj = (DemoClass)newDomain.CreateInstanceFromAndUnWrap("c:\\ClassLib.dll", "ClassLib.DemoClass");

注意:obj是在当前的默认应用程序域声明的;而类型的实例(对象本身)却在新创建的应用程序域NewDomain中创建的。这样就出现了一种尴尬的情况:而默认情况下AppDomain是彼此隔离的,我们不能直接在一个应用程序中引用另一个应用程序域中的对象,所以这里便会引发异常。

跨AppDomain运行代码:

void Main()
{
//建立新的应用程序域对象
AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
CrossAppDomainDelegate crossAppDomainDelegate = new CrossAppDomainDelegate(MyCallBack); newAppDomain.DoCallBack(crossAppDomainDelegate); newAppDomain.DomainUnload += (obj, e) =>
{
Console.WriteLine("New Domain unload!");
}; AppDomain.Unload(newAppDomain);
}
static public void MyCallBack()
{
string name = AppDomain.CurrentDomain.FriendlyName;
for (int n = 0; n < 4; n++)
Console.WriteLine(string.Format(" Do work in {0}........", name));
}

五、封值传送,Serializable

我们可以将对象标记为可序列化,将对象本身由另一应用程序域(远程)传递到本地应用程序域中。

原理: 先在远程创建对象,接着将对象序列化,然后传递对象,在本地进行反序列化,最后还原对象。

当位于ConsoleApp.exe的obj引用NewDomain中创建的对象时,.Net将NewDomain中对象的状态进行复制、序列化,然后在ConsoleApp.exe中重新创建对象,还原状态,并通过代理来对对象进行访问。这种跨应用程序域的访问方式叫做 传值封送(Marshal by value)

[Serializable]
public class DemoClass { /*略*/ }

六、传引用封送,MarshalByRefObject

让对象依然保留在远程(本例为NewDomain中),而在客户端仅创建代理,上面已经说了代理的接口和远程对象完全相同,所以客户端以为仍然访问的是远程对象,当客户端调用代理上的方法时,由代理将对方法的请求发送给远程对象,远程对象执行方法请求,最后再将结果传回。这种方式叫做 传引用封送(Marshal by reference)

对象或者对象引用在传递的过程中,是以一种包装过的状态(warpper state)进行传递。所以在创建对象时,要解包装,因此在CreateInstanceAndUnWrap()方法后多了一个AndUnWrap后缀,实际上UnWrap还包含一个创建代理的过程。

让对象继承自MarshalByRefObject基类,实现传引用封送。

public class DemoClass:MarshalByRefObject {/*略*/}

上下文绑定对象:ContextBoundObject

当系统需要对象使用消息接收器机制的时候,即可使用ContextBoundObject类。ContextBoundObject继承了MarshalByRefObject类,保证了它的子类都会通过透明代理被访问。

一般类所建立的对象为上下文灵活对象(context-agile),它们都由CLR自动管理,可存在于任意的上下文当中。而 ContextBoundObject 的子类所建立的对象(称作上下文绑定对象)只能在建立它的对应上下文中正常运行,此状态被称为上下文绑定。其他对象想要访问ContextBoundObject 的子类对象时,都只能通过代透明理来操作。ContextBound还有一个Synchronization特性,此特性会保证ContextBound对象被加载到一个线程安全的上下文当中运行。

七、应用程序域、进程与线程关系。

概述:

  1. 进程之间彼此是完全隔绝的,而线程与运行在相同进程的其他线程共享堆heap内存。
  2. 在应用程序域和线程之间没有一对一的关联。

进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文(Context)的关系如图5.0。

一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用。

应用程序域 System.AppDomain,动态加载程序集的更多相关文章

  1. C# 动态加载程序集信息

    本文通过一个简单的实例,来讲解动态加载Dll需要的知识点.仅供学习分享使用,如有不足之处,还请指正. 在设计模式的策略模式中,需要动态加载程序集信息. 涉及知识点: AssemblyName类,完整描 ...

  2. C# 动态加载程序集dll (实现接口)

    一.程序集(接口程序集):LyhInterface.Dll namespace LyhInterface { public interface ILyhInterface { void Run(); ...

  3. C#动态加载程序集(转)

    C#动态加载程序集 今天在看网络上的一篇关于‘.NET应用自动部署窗体技术’.NET的自动部署技术构造在.NET框架之中,它使得应用程序能够通过HTTP连接从远程服 务器按需下载程序集.有了这个功能, ...

  4. .Net Core 通过依赖注入和动态加载程序集实现宿程序和接口实现类库完全解构

    网上很多.Net Core依赖注入的例子代码,例如再宿主程序中要这样写: services.AddTransient<Interface1, Class1>(); 其中Interface1 ...

  5. Assembly.Load动态加载程序集而不占用文件 z

    方式一:占用文件的加载 Assembly assembly = Assembly.Load(path); 用上面的方法可以动态的加载到dll,但是用这种方法加载到的dll一直到程序运行结束都是占用的d ...

  6. C# 反射实现动态加载程序集

    原文:https://blog.csdn.net/pengdayong77/article/details/47622235 在.Net 中,程序集(Assembly)中保存了元数据(MetaData ...

  7. 关于c#动态加载程序集的一些注意事项

    Assembly下有LoadFile,LoadFrom等方法可以加载程序集. LoadFile只加载你给定路径的那个dll,LoadFrom会自动加载依赖的dll. 如:A依赖B,LoadFile(& ...

  8. asp.net动态加载程序集创建指定类的实例及调用指定方法

    以下类中有三个方法: LoadAssembly:加载指定路径的程序集 GetInstance:根据Type动态获取实例,用泛型接到返回的类型 ExecuteMothod:执行实例中的指定方法 /// ...

  9. C# 动态加载程序集

    定义动态程序集 namespace DynamicAssembly { public class CodeDriver : MarshalByRefObject { private string pr ...

随机推荐

  1. Spring中声明式事务的注解@Transactional的参数的总结(REQUIRED和REQUIRES_NEW的与主方法的回滚问题)

    一.事务的传播行为1.介绍 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行.2.属性 事务的传播行为可以由传 ...

  2. JSP的部分知识(一)

    通过Servlet进行整个网站的开发是可以的. 不过在Servlet中输出html代码,特别是稍微复杂一点的html代码,就会给人一种很酸爽的感觉. 如果能够直接使用Html代码,然后在html中写j ...

  3. AVR单片机教程——点亮第一个LED

    做了这么多准备,我们终于可以开始用开发板做点事了. 单片机编程与计算机编程有一些不同点.程序都要有零个或多个输入.一个或多个输出,这是两者都有的,但是计算机编程的输入输出主要靠控制台,而单片机没有. ...

  4. springboot基础、注解等

    SpringBoot 1.springboot概念 Spring Boot是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. ...

  5. Python中turtle库的使用

    Turtle图形库 Turtle库是Python内置的图形化模块,属于标准库之一,位于Python安装目录的lib文件夹下,常用函数有以下几种: 画笔控制函数 penup():抬起画笔: pendow ...

  6. 《MySQL数据库从入门到精通》 高级运维人才的必备书籍

    众所周知,每年就业市场都会迎来千万量级的高校毕业生,然而企业招工难和毕业生就业难的矛盾却一直没有得到很好地解决.究其原因,主要矛盾还是在于传统的学历教育与企业实际需求相脱节.为了杜绝高校毕业生求职时常 ...

  7. 简单即时通讯、聊天室--java NIO版本

    实现的功能: 运行一个服务端,运行多个客户端.在客户端1,发送消息,其余客户端都能收到客户端1发送的消息. 重点: 1.ByteBuffer在使用时,注意flip()方法的调用,否则读取不到消息. 服 ...

  8. 优秀的java 社区

    并发编程网 - ifeve.com InfoQ - 促进软件开发领域知识与创新的传播开源中国 - 找到您想要的开源项目,分享和交流IBM developerWorks 中国 : IBM's resou ...

  9. 本地安装SQL Server 2017 Express和Microsoft SQL Server Management Studio 18.1

    sqlserver下载链接:https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 这个安装的是免费版的Express,当然也可 ...

  10. iOS - SceneKit 3D引擎初探

    最近到处搜集资料研究3D最后还是决定锁定OC框架,找到的学习资料随后慢慢整理 SceneKit 是一个OC 框架,开始之前,先熟悉一下SceneKit 的三维坐标系: 很清楚的看到,SceneKit ...