.NET(C#): MarshalByRefObject的非静态方法和AppDomain.DoCallBack

首先,静态类型是对所有AppDomain可见且是只保存一份的,而如果在AppDomain.DoCallBack中使用了非静态方法调用,那么目标对象就必须被封送,几年前写过一篇文章:AppDomain.DoCallBack方法和Lambda表达式,就是讲的如果在DoCallBack方法中使用了捕获外部变量的Lambda表达式,那么由于编译器会把此类Lambda调用编译成生成类型的实例方法调用,所以会造成异常抛出(因为生成类型无法被封送)。也写过另一篇关于AppDomain中CreateInstance方法和对象的封送:多个AppDomain下有趣的封送对象。今天来简单看下AppDomain类型的DoCallBack方法回调对象方法的情况。

回调按引用封送的对象(即MarshalByRefObject)方法时,DoCallBack回调完全等效于直接调用代码,对象根本不会被封送,也不存在透明代理,如下代码:

class Program: MarshalByRefObject

{

    //用来存储第一个Program对象

    static Program FirstProgram;

 

    static void Main()

    {

        var program = FirstProgram = new Program();

        program.Doo();

    }

 

    //创建新的AppDomain

    void Doo()

    {

        var domain = AppDomain.CreateDomain("另一个AppDomain");

        domain.DoCallBack(Callback);

        AppDomain.Unload(domain);

    }

 

    //AppDomain.DoCallBack回掉方法

    void Callback()

    {

        Console.WriteLine("来自 {0}", AppDomain.CurrentDomain.FriendlyName);

        Console.WriteLine("同一个对象:{0}", FirstProgram == this);

        Console.WriteLine("是透明代理:{0}", System.Runtime.Remoting.RemotingServices.IsTransparentProxy(this));

    }

}

输出:

来自 Mgen.exe

同一个对象:True

是透明代理:False

可以看到,方法在主AppDomain中执行。

 

而如果对象是按值封送的话,即可序列化的对象,DoCallBack会封送对象。

我们可以直接把上面的Program类型去掉继承自MarshalByRefObject类型,然后加上[Serializable]特性,这样他就可以按值封送了,如下:

[Serializable]

class Program

{

    //和上面相同

}

再次运行,这次会输出:

来自 另一个AppDomain

同一个对象:False

是透明代理:False

对象会被封送到另一个AppDomain中,由于是按值封送,因此也可以理解成是深度复制到另一个AppDomain中的。

 

OK,那么怎样解决MarshalByRefObject对象无法通过实例回调进行封送的问题呢?答案就是不通过实例方法,用静态方法作为DoCallBack参数,然后使用AppDomain的Get/SetData方法来传递也就是封送这个对象。

如下代码:

class Program: MarshalByRefObject

{

    //用来存储第一个Program对象

    static Program FirstProgram;

 

    static void Main()

    {

        var program = FirstProgram = new Program();

        program.Doo();

    }

 

    //创建新的AppDomain

    void Doo()

    {

        var domain = AppDomain.CreateDomain("另一个AppDomain");

        domain.SetData("TestData", this);

        //使用静态的回调方法

        domain.DoCallBack(StaticCallBack);

        AppDomain.Unload(domain);

    }

 

    //静态回调方法

    static void StaticCallBack()

    {

        var program = (Program)AppDomain.CurrentDomain.GetData("TestData");

        Console.WriteLine("来自 {0}", AppDomain.CurrentDomain.FriendlyName);

        Console.WriteLine("同一个对象:{0}", FirstProgram == program);

        Console.WriteLine("是透明代理:{0}", System.Runtime.Remoting.RemotingServices.IsTransparentProxy(program));

    }

}

输出:

来自 另一个AppDomain

同一个对象:False

是透明代理:True

对象被成功封送,同时透明代理判断返回True!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。