应用程序域 System.AppDomain,动态加载程序集
一、概述
使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll),这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。处理asp.net所涉及的类大多数定义在System.Web程序集中。
- 当exe程序集加载完毕,.Net会在当前进程中创建一个默认应用程序域,这个应用程序域的名称与程序集名称相同。默认应用程序域不能被卸载,并且与其所在的进程同生共灭。
- 应用程序域只是允许它所加载的程序集访问由.Net Runtime所提供的服务。这些服务包括托管堆(Managed Heap),垃圾回收器(Garbage collector),JIT 编译器等.Net底层机制.
- 一个应用程序域中如果出现了致命错误导致崩溃,只会影响其本身,而不会影响到其他应用程序域。实现了错误隔离。
应用程序域是进程中承载程序集的逻辑分区,在应用程序域中存在更细粒度的用于承载.NET对象的实体,即.NET上下文Context。
所有的.NET对象都存在于.NET上下文当中,每个AppDomain当中至少存在一个默认上下文(context0),:
优点
托管程序为什么要使用应用程序域呢?概括其优点如下:
- 在一个应用程序中出现的错误不会影响其他应用程序。
- 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。
- 能够在不停止整个进程的情况下停止单个应用程序。
- 使用应用程序域时,可以卸载在单个应用程序中运行的代码。
- 在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。
属性
- 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对象被加载到一个线程安全的上下文当中运行。
七、应用程序域、进程与线程关系。
概述:
- 进程之间彼此是完全隔绝的,而线程与运行在相同进程的其他线程共享堆heap内存。
- 在应用程序域和线程之间没有一对一的关联。
进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文(Context)的关系如图5.0。
一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用。
应用程序域 System.AppDomain,动态加载程序集的更多相关文章
- C# 动态加载程序集信息
本文通过一个简单的实例,来讲解动态加载Dll需要的知识点.仅供学习分享使用,如有不足之处,还请指正. 在设计模式的策略模式中,需要动态加载程序集信息. 涉及知识点: AssemblyName类,完整描 ...
- C# 动态加载程序集dll (实现接口)
一.程序集(接口程序集):LyhInterface.Dll namespace LyhInterface { public interface ILyhInterface { void Run(); ...
- C#动态加载程序集(转)
C#动态加载程序集 今天在看网络上的一篇关于‘.NET应用自动部署窗体技术’.NET的自动部署技术构造在.NET框架之中,它使得应用程序能够通过HTTP连接从远程服 务器按需下载程序集.有了这个功能, ...
- .Net Core 通过依赖注入和动态加载程序集实现宿程序和接口实现类库完全解构
网上很多.Net Core依赖注入的例子代码,例如再宿主程序中要这样写: services.AddTransient<Interface1, Class1>(); 其中Interface1 ...
- Assembly.Load动态加载程序集而不占用文件 z
方式一:占用文件的加载 Assembly assembly = Assembly.Load(path); 用上面的方法可以动态的加载到dll,但是用这种方法加载到的dll一直到程序运行结束都是占用的d ...
- C# 反射实现动态加载程序集
原文:https://blog.csdn.net/pengdayong77/article/details/47622235 在.Net 中,程序集(Assembly)中保存了元数据(MetaData ...
- 关于c#动态加载程序集的一些注意事项
Assembly下有LoadFile,LoadFrom等方法可以加载程序集. LoadFile只加载你给定路径的那个dll,LoadFrom会自动加载依赖的dll. 如:A依赖B,LoadFile(& ...
- asp.net动态加载程序集创建指定类的实例及调用指定方法
以下类中有三个方法: LoadAssembly:加载指定路径的程序集 GetInstance:根据Type动态获取实例,用泛型接到返回的类型 ExecuteMothod:执行实例中的指定方法 /// ...
- C# 动态加载程序集
定义动态程序集 namespace DynamicAssembly { public class CodeDriver : MarshalByRefObject { private string pr ...
随机推荐
- Vue零碎总结
1.Vue指令里的bind钩子是生成了vnode,但是将它插入/更新到浏览器dom树之前的操作,因此对于一些需要插入dom树后执行的操作它是不支持的,如el.focus()方法,这些要放在insert ...
- 将linux的root用户的家目录由/root切换为/home/root
步骤1,先以root登录,然后创建目录/home/root步骤2,vi /etc/passwd里root用户的家目录为/home/root:步骤3,cp -rf /root/ /home/将原来的ro ...
- LeetCode 643. 子数组最大平均数 I(Maximum Average Subarray I)
643. 子数组最大平均数 I 643. Maximum Average Subarray I 题目描述 给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. LeetCo ...
- Echarts 不能百分比显示或显示有问题
1,设折线图宽为100%(如:容器div的class=“RiBarBot”宽为880px),刚初始化时隐藏折线图(或后期刷新.隐藏与显示折线图时),当点击显示折线图时,获取到的宽只有100px,并不是 ...
- linux 加载新的磁盘(卷组)
pvcreate /dev/vdbvgcreate datavg /dev/vdblvcreate -n datalv -L 99.8G datavgmkfs.ext3 /dev/datavg/dat ...
- SQL 先固定特殊的几行数据之外再按照某一字段排序方法(CASE 字段排序(CASE WHEN THEN)
查询用户表的数据,管理员用户始终在最前面,然后再按照CreateTime排序: SELECT TOP * FROM [dbo].[User] WHERE ParentID = '**' ORDER B ...
- Linux基础-08-进程控制
1. 系统监视和进程控制工具—top和free 1) top命令的功能:top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. 2) ...
- 接口缓存--把接口放在redis数据库中,减少访问量
针对访问量大,且数据较固定的接口,建议建立接口缓存,建立了缓存之后,不会再直接去访问接口了. 比如下面的轮播图接口,每刷新一下首页都会访问一下轮播图接口,所以我们用接口缓存来处理,减少访问量. 视图模 ...
- OpenCV学习笔记3
OpenCV学习笔记3 图像平滑(低通滤波) 使用低通滤波器可以达到图像模糊的目的.这对与去除噪音很有帮助.其实就是去除图像中的高频成分(比如:噪音,边界).所以边界也会被模糊一点.(当然,也有一些模 ...
- SAS学习笔记55 RTF输出标题级别的设置
很多人都喜欢将表格的标题通过report中的compute before _page_ 添加到table的前面,但是这种方式并不能直接定义标题的级别,需要后期进行表格的加工. 在使用proc repo ...