C#应用程序域

应用程序域(AppDomain)是.NET程序中的一种资源隔离技术。

通过System.AppDomain类,可以进行应用程序域的操作。

默认域

程序运行时,会创建一个默认应用程序域。

大部分情况,我们只需要使用默认域即可。

通过静态属性AppDomain.CurrentDomain可以获取当前域的信息。

创建

通过AppDomain.CreateDomain可以创建一个域。

AppDomain newDomain = AppDomian.CreateDomain("new domain");

在域中执行代码

通过DoCallBack方法可以在另一个域执行代码。

    var newDomain = AppDomain.CreateDomain("new domain");

    newDomain.DoCallBack(() =>
    {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        Console.WriteLine("ok");
    });

处理域中的异常

如果域中的代码没有处理,异常会抛到最外面,导致程序退出。

    var newDomain = AppDomain.CreateDomain("new domain");

    newDomain.DoCallBack(() =>
    {
        throw new NotImplementException();
    });

可以通过UnhandledException来对未处理异常进行处理。

    var newDomain = AppDomain.CreateDomain("new domain");
    newDomain.UnhandledException+=(s,e)=>
    {
        
    };
    newDomain.DoCallBack(() =>
    {
        throw new NotImplementException();
    });

注意的是,UnhandledException不能够阻止程序终止。

通过FirstChanceException事件可以在每次产生异常时,比catch语句更先的到通知。

FirstChanceException处理程序中,只能够查看异常,不能够阻止异常。

应用程序域的作用

应用程序域是对程序运行中的资源进行隔离。一个域不可以直接访问另一个域的数据。

跨域访问

public class MyClass
{

}

class Program{
MyClass obj = new MyClass();

static void Main(string[] args)
{
        var newDomain = AppDomain.CreateDomain("new domain");

        newDomain.DoCallBack(() =>
        {
            Console.WriteLine(obj);
        });
}
}

在新的应用程序域中,不能够访问obj变量,因为它在默认域定义。

如果要跨域访问变量,需要变量符合一定的要求。

注意:不能在 DoCallBack的委托中使用闭包引用局部变量,因为编译生产的类型不符合跨域访问要求。

复制

类型需要可序列化。这样,跨域访问的时候其实访问的是经过序列化和反序列化的副本。

.NET中的基本类型都是些了可序列化。


[Serialize]
public class MyClass
{

}

class Program{

	static void Main(string[]args)
    {
            var newDomain = AppDomain.CreateDomain("new domain");
            MyClass obj = (MyClass)newDomain.CreateInstanceAndUnwrap("LearnCSharp", "LearnCSharp.MyClass");

            obj.F();
    }
}

代理

类型需要继承自System.MarsharByRefObject。这样,跨域访问的使用是通过代理。

using System.Remoting;

public class MyClass :MarsharByRefObject
{
    public void F()
    {
    }
}

class Program{

	static void Main(string[]args)
    {
            var newDomain = AppDomain.CreateDomain("new domain");
            MyClass obj = (MyClass)newDomain.CreateInstanceAndUnwrap("LearnCSharp", "LearnCSharp.MyClass");

            obj.F();//调用的是代理的方法,代理会在newDomain域执行代码。
            Console.WriteLine(obj.GetType());//调用的也是代理方法。obj欺骗了其类型。
            Console.WriteLine(RemotingServices.IsTransparentProxy(obj));//True
    }
}

通过System.Runtime.Remoting.RemotingService.IsTransparentProxy可以判断是否是代理。

obj对象方法调用是通过代理在另一个域调用。

域与静态方法

在不同的域的静态成员,是不同的静态成员。

public class MyClass :MarsharByRefObject
{
    public static int value =10;
       static MyClass()
        {
            Console.WriteLine("静态构造函数" + AppDomain.CurrentDomain.FriendlyName);
        }
}
class Program{

	static void Main(string[]args)
    {
            var newDomain = AppDomain.CreateDomain("new domain");
            MyClass.i = 200;
            newDomain.DoCallback(()=>{
            	Console.WriteLine(MyClass.i);//10
            });
    }
}

类型的静态构造函数会执行两次。

在不同的域,会创建不同的类型。

域中立

mscorlib程序集是.NET主要类型的所在位置,它在不同的域公用。

线程和域

线程是代码的执行单元,域是对资源的划分。

一个线程可以在多的域执行。但同一时刻,一个线程只在一个域。通过DoCallback线程将会从当前域进入新域;在调用代理对象的方法,也会进入新域。

一个域可以创建多个的线程。

卸载域

通过AppDomain.Unload静态方法卸载域。

AppDomain newDomain = AppDomian.CreateDomain("new domain");
AppDomain.Unload(newDomain);

当域被卸载之后,再通过代理对象调用方法,会报AppDomainUnloadedException

.NET Core中的域

在.NET Core中,域只提供了最基础的支持。不能够创建新的域,只能使用默认域。虽然相关的方法存在,但是不被支持。

建议使用多进程替代域。