一、概述

使用.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. myeclipse 相关问题

    [如何设置 Tab 键为四个空格] https://blog.csdn.net/QQ826688096/article/details/90543252

  2. 稀疏数组(java实现)

    1.稀疏数组 当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组. 稀疏数组的处理方法是: 1.1记录数组一共有几行几列,有多少个不同的值 1.2把具有不同值的元素的行列 ...

  3. hexo 个人博客搭建

    Hexo 小插曲介绍 虽然标题是第一次写博客. 但是我这个困难户至少挣扎了1年多了, 一直下不去手.今天可算是开了个头. 贵在坚持吧 抽时间介绍我的hexo安装历程吧,今天实在是有点困了,要睡觉了. ...

  4. 【坑】前端使用ajax异步请求以后,springMvc拦截器跳转页面无效

    文章目录 前言 `$.ajaxSetup( )` 后记 前言 本文着重解决前后端分离开发的页面调整问题. 笔者,在做一个需求,需要对访问网站,但是没有登录的用户进行拦截,将他们重定向到首页. 很简单的 ...

  5. P1308(字符串类,处理字符串查找)

    题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给 ...

  6. 定期备份和清理gitlab文件

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2019-12-05 14:39 # @Author : Anthony # @Emai ...

  7. C/C++中vector与list的区别

    1.vector数据结构vector和数组类似,拥有一段连续的内存空间,并且起始地址不变.因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存 ...

  8. Linux追加磁盘扩展

    一:查看磁盘空间信息: fdisk -l 查看当前的系统的磁盘空间的情况: 二:增加分区: fdisk /dev/sda 键入n,增加一个分区,得到: 键入 p,主分区,并键入3(编号): 默认起始扇 ...

  9. 从C++到Qt(舍弃IDE或qmake、cmake等工具的束缚,尝试通过几个例子)

    Qt 是 C++ 的库,Qt 在 ansi C++ 的基础上进行了一点扩展. 但国内似乎比较浮躁,学Qt的很多连基本的C++如何编译似乎都不太清楚.本文舍弃IDE或qmake.cmake等工具的束缚, ...

  10. 音视频入门-09-RGB&YUV互转-使用开源库

    * 音视频入门文章目录 * 介绍开源库 使用第三方开源库来简化开发,屏蔽一些底层的复杂度,节省大量编写代码的时间. libyuv: Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转 ...