应用程序域,你在网上可以查到它的定义,凡是概念性的东西,大伙儿只需要会搜索就行,内容看了就罢,不用去记忆,更不用去背,“名词解释”是大学考试里面最无聊最没水平的题型。

简单地说,应用程序域让你可以在一个进程中将某些代码隔离执行,相同的代码可以在不同的应用程序域中独立执行,互不干扰。也就是我做我的事,他干他的活,互不影响。

一、隔离性

先来看看,应用程序域之间的隔离是怎么一回事,请原谅老周的理论水平低下,从来不会长篇大论地叙述,老周最大的特长是写代码来说明问题。所以,关于应用程序域之间的隔离性,还是写代码来展示吧。

咱们来写一个静态类。

    public static class Demo
{
public static string Title { get; set; } = "<NULL>";
public static string Content { get; set; } = "<NULL>";
}

经验告诉我们,静态成员是基于类型的,它的调用不需要实例化,所以,静态成员的值一般都可以全局使用。大伙儿也应该知道,应用程序中至少会存在一个应用程序域,在应用程序运行时自动创建。

下面我们来做一个神奇实验,这个实验的结果会让你意外的。

先在应用程序自动创建的应用程序域中为静态类的静态成员赋值。

            // 在应用程序自动创建的应用程序域中赋值
Demo.Title = "大甩卖";
Demo.Content = "二手豆腐渣商品房,200元一平米。";

好,然后,我们创建一个新的应用程序域,给它一个名字,叫no face吧。

            // 创建新的应用程序域
AppDomain newDomain = AppDomain.CreateDomain("no_face");

AppDomain有一个方法叫DoCallBack,方法的调用需要一个委托作为参数,委托关联的方法无参数,返回类型为void。这个方法的作用是在指定的应用程序域中执行委托所关联的代码。

现在,我们在新创建的应用程序域中访问Demo静态类的成员,看看会输出什么。

            newDomain.DoCallBack(() =>
{
string msg = $"【今日头条】\n\n标题:{Demo.Title}\n正文:{Demo.Content}";
Console.WriteLine(msg);
});

运行程序后,你会看到以下输出。

这说明了啥?说明两个应用程序域之间是独立的,虽然静态类型好像是“全局”的,但它是相对于同一个应用程序域而言的。从刚刚的例子中看到,为静态成员赋值是在程序默认创建的应用程序域中执行的,而对于新创建的应用程序域中是隔离的,也就是说,赋值在新的应用程序域中是不存在的,所以输出的仍是默认值。

二、执行带入口点的程序集

AppDomain类公开了 ExecuteAssembly 和 ExecuteAssemblyByName 方法,没有带“Name”的方法是从程序集所在的文件中加载,而带有“Name”的方法是通过程序集名称来加载的,至于用哪个方法,你看着办吧。

不过,调用这两个方法执行的程序集必须要有入口点的,所以不能是.dll,类库不能定义入口点。

声明一个类,然后记得在类中定义Main方法。

    public class Class1
{
static int Main(string[] args)
{
Console.WriteLine("程序集正在{0}域中运行。", AppDomain.CurrentDomain.FriendlyName);
if (args.Length > )
{
Console.WriteLine("参数:{0}", string.Join(",", args));
}
return ;
}
}

如果新建的项目是类库,可以打开项目属性,把项目类型改为 Windows应用程序。

然后,选择一个入口点(带有Main方法的类型)。

回到主项目,创建新的应用程序域,然后执行一个带入口点的程序集。

            string file = "..\\..\\..\\TestAssem\\Bin\\Debug\\TestAssem.exe";
AppDomain newappd = AppDomain.CreateDomain("new-appd");
int r = newappd.ExecuteAssembly(file, new string[] { "arg1", "arg2", "arg3" });
Console.WriteLine("执行结果:{0}", r);

ExecuteAssembly 方法的返回值,就是要执行程序集的Main方法的返回值。可以通过一个字符串数组来向要执行的程序集传递参数,参数会传到目标Main方法中。

看看执行结果。

三、跨域封送

在一个应用程序域中执行代码,除了前面提到过 DoCallback 方法,还可以用 CreateInstanceXXXX 方法,这些方法是直接创建一个在指定应用程序域执行类型的实例,由于实例是跨应用程序域传送的,所以需要封送,被创建的实例会用一个ObjectHandle 封装,然后你要调用Unwrap方法来解封,才能得到对象实例。如果希望一次性完成这个过程,可以调用带有 AndUwrap的方法,这样在创建实例后会自动封送到当前应用程序域,并自动解封。

使用CreateInstanceXXX方法,主要是可以把其他应用程序域中的变量传到当前的应用程序域,而 DoCallback 方法适用于可以独立在另一应用程序域执行的代码,当前应用程序域无需引用变量。

假设,我定义这么个类。

namespace OXX
{
public class Player
{
public void Play()
{
Console.WriteLine($"正在应用程序域“{AppDomain.CurrentDomain.FriendlyName}”上玩耍。");
}
}
}

然后,在新创建的应用程序域中执行它。

            AppDomain newdomain = AppDomain.CreateDomain("new_face");

            Type objtype = typeof(OXX.Player);
OXX.Player obj = (OXX.Player)newdomain.CreateInstanceAndUnwrap(objtype.Assembly.FullName, objtype.FullName);
obj.Play();

代码比较简单,先创建应用程序域,然后创建类型实例,接着调用类型实例成员。但,这样一运行就会发生错误的,因为类型实例是要从一个应用程序域传送到另一个域,而每个域的代码是相互隔离的,因此,实例传递需要让类型可序列化,这样才能从一个应用程序域复制到另一个应用程序域。

第一种方法,就是加上Serializable 特性。

    [Serializable]
public class Player
{
public void Play()
{ }
}

这样声明后,对象实例会按值来传递,即实例会从 new_face 应用程序域复制一份到当前域,所以,当你运行应用程序后,你会发现,输出的应用程序域名称是默认的应用程序域。

因为实例被复制了一份,而Play方法是在默认应用程序域中调用的,所以它输出的当前应用程序域名就是这个默认的域的名字,这与值类型的传递差不多,对象自身会被复制。

如果你想让 obj 变量能在新的应用程序域中执行,可以采用第二种方法,就是不使用 Serializable 特性,改为从 MarshalByRefObject 类派生,这样就能实现引用传递,此时传送的是对象实例的引用,而不是复制一份。

    public class Player : MarshalByRefObject
{
public void Play()
{
……
}
}

现在,再次运行示例,发现输出的是新创建的应用程序域的名字,而不再是默认域的名字了。

好了,今天的话题就讨论到此了。

示例源代码下载

【.net 深呼吸】跨应用程序域执行程序集的更多相关文章

  1. 【.net 深呼吸】限制执行代码的权限

    前面好几篇文章,老周都跟大伙伴们聊了跟应用程序域有关的话题,干脆咱们一聊到底吧,做学问就应该这样,有恒心. App Domain的创建新应用程序域的方法中,有一个特殊的重载: public stati ...

  2. 解析.NET对象的跨应用程序域访问--AppDomain(上篇)

    在目前的项目开发中,分布式开发已经逐渐成为主流.一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目.这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验.只有 ...

  3. 解析.NET对象的跨应用程序域访问(下篇)

    转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDomain的相关信息,在本篇博文 ...

  4. 解析.NET对象的跨应用程序域访问(上篇)

    在目前的项目开发中,分布式开发已经逐渐成为主流.一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目.这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验.只有 ...

  5. NET对象的跨应用程序域

    NET对象的跨应用程序域 转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDoma ...

  6. C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

    AppDomain 表示应用程序域,它是一个应用程序在其中执行的独立环境.每个应用程序只有一个主应用程序域,但是一个应用程序可以创建多个子应用程序域. 因此可以通过 AppDomain 创建新的应用程 ...

  7. WPF 跨应用程序域的 UI(Cross AppDomain UI)

    为自己写的程序添加插件真的是一个相当常见的功能,然而如果只是简单加载程序集然后去执行程序集中的代码,会让宿主应用程序暴露在非常危险的境地!因为只要插件能够运行任何一行代码,就能将宿主应用程序修改得天翻 ...

  8. 跨应用程序域(AppDomain)的单例(Singleton)实现

    转载自: 跨应用程序域(AppDomain)的单例(Singleton)实现 - CorePlex代码库 - CorePlex官方网站,Visual Studio插件,代码大全,代码仓库,代码整理,分 ...

  9. [CLR via C#]1.4 执行程序集的代码

    原文:[CLR via C#]1.4 执行程序集的代码 1. 托管程序集同时包含元数据和IL.IL是与CPU无关的机器语言.可将IL是为一种面向对象的机器语言. 2. IL也是能使用汇编语言来写的,M ...

随机推荐

  1. WebForm获取GET或者POST参数到实体的转换,ADO.NET数据集自动转换实体

    最近在修改维护以前的webform项目(维护别人开发的.....)整个aspx没有用到任何的控件,这个我也比较喜欢不用控件所以在提交信息的时候需要自己手动的去Request.QueryString[] ...

  2. 简记用ArcGIS处理某项目需求中数据的步骤

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 项目需求涉及如下几个步骤: a.矢量化 b.获取范围内要素 ...

  3. 那些年【深入.NET平台和C#编程】

    一.深入.NET框架 1..NET框架具有两个组件:CLR(公共语言运行时)和FCL(框架类库),CLR是.NET框架的基础 2.框架核心类库: System.Collections.Generic: ...

  4. ActiveRecord模式整理

    DAO Data Access Object,数据访问对象 DAO是一个软件设计的指导原则,在核心J2EE模式中是这样介绍DAO模式的:为了建立一个健壮的J2EE应用,应该将所有对数据源的访问操作抽象 ...

  5. Flexible 弹性盒子模型之CSS flex-basis 属性

    实例 设置第二个弹性盒元素的初始长度为 80 像素: div:nth-of-type(2){flex-basis:80px;}   效果预览 浏览器支持 表格中的数字表示支持该属性的第一个浏览器的版本 ...

  6. 解决maven下载jar慢的问题(如何更换Maven下载源)

    修改 配置文件 maven 安装 路径 F:\apache-maven-3.3.9\conf 修改 settings.xml 在 <mirrors> <!-- mirror | Sp ...

  7. [AlwaysOn Availability Groups]健康模型 Part 1——概述

    健康模型概述 在成功部署AG之后,跟踪和维护健康状况是很重要的. 1.AG健康模型概述 AG的健康模型是基于策略管理(Policy Based Management PBM)的.如果不熟悉这个特性,可 ...

  8. samba服务

    安装samba服务步骤ps -e 查看进程ps -e | grep 文件名 管道符的使用rpm -qa 安装包的查看rpm -qa | grep samba 抓Samba安装包 注释:包与包之间有依赖 ...

  9. 技术笔记:XMPP之openfire+spark+smack

    在即时通信这个领域目前只找到一个XMPP协议,在其协议基础上还是有许多成熟的产品,而且是开源的.所以还是想在这个领域多多了解一下. XMPP协议:具体的概念我就不写了,毕竟这东西网上到处是.简单的说就 ...

  10. 我的MYSQL学习心得(十一) 视图

    我的MYSQL学习心得(十一) 视图 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...