17

我想跨 AppDomains 使用一个对象。

为此,我可以使用 [Serializeable] 属性:

[Serializable]
class MyClass
{
    public string GetSomeString() { return "someString" }
}

或 MarshalByRefObject 的子类:

class MyClass: MarshalByRefObject
{
    public string GetSomeString() { return "someString" }
}

在这两种情况下,我都可以像这样使用该类:

AppDomain appDomain = AppDomain.CreateDomain("AppDomain");
MyClass myObject = (MyClass)appDomain.CreateInstanceAndUnwrap(
                   typeof(MyClass).Assembly.FullName,
                   typeof(MyClass).FullName);
Console.WriteLine(myObject.GetSomeString());

为什么这两种方法似乎具有相同的效果?两种方法有什么区别?我什么时候应该偏爱一种方法而不是另一种方法?

编辑:表面上我知道两种机制之间存在差异,但是如果有人从灌木丛中跳出来问我这个问题,我无法给他正确的答案。这些问题是相当开放的问题。我希望有人能比我做得更好。

4

4 回答 4

21

使用 MarshallByRef 将在远程 AppDomain 中执行您的方法。当您将 CreateInstanceAndUnwrap 与 Serializable 对象一起使用时,会将该对象的副本复制到本地 AppDomain,因此任何方法调用都将在本地 AppDomain 中执行。

如果您想要在 AppDomain 之间进行通信,请使用 MarshallByRef 方法。

一个例子:

using System;
using System.Reflection;

[Serializable]
public class SerializableClass
{
    public string WhatIsMyAppDomain()
    {
        return AppDomain.CurrentDomain.FriendlyName;
    }
}

public class MarshallByRefClass : MarshalByRefObject
{
    public string WhatIsMyAppDomain()
    {
        return AppDomain.CurrentDomain.FriendlyName;
    }
}    

class Test
{

    static void Main(string[] args)
    {
        AppDomain ad = AppDomain.CreateDomain("OtherAppDomain");

        MarshallByRefClass marshall = (MarshallByRefClass)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "MarshallByRefClass");
        SerializableClass serializable = (SerializableClass)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "SerializableClass");

        Console.WriteLine(marshall.WhatIsMyAppDomain());
        Console.WriteLine(serializable.WhatIsMyAppDomain());

    }
}

当您从 MarshallByRef 对象调用 WhatIsMyAppDomain 时,此代码将显示“OtherAppDomain”,当您从 Serializable 对象调用时,将显示您的默认 AppDomain 名称。

于 2009-03-01T12:54:18.530 回答
10

这些方法具有截然不同的效果。

使用 MarshalByRef 版本,您正在创建对象的 1 个实例。它将位于新创建的 AppDomain 中。对对象的所有访问都是通过TransparentProxy完成的。

使用 Serializable 版本,您将创建对象的 2 个实例。一个是在新创建的 AppDomain 中创建的。CreateInstanceAndUnwrap 调用将序列化这个对象并在原始应用程序域中反序列化它。这将创建完全独立于第一个版本的对象的第二个版本。事实上,下一次 GC 几乎肯定会消除原始对象,而您将只剩下一个实例。

于 2009-03-01T18:26:19.603 回答
6

为什么这两种方法具有相同的效果?

它们没有相同的效果。

随着MarshalByRefObject您跨 AppDomain 边界引用一个对象。[Serializable]正在制作对象的副本。如果对象的状态在子域中被修改然后再次检查(或Console.WriteLine在子 AppDomain 内部执行),这将显示出来。

于 2009-03-01T12:22:03.660 回答
2

MarshalByRefValueSerializable为远程处理/跨 AppDomain 通信实现不同的语义。MarshalByRefValue本质上通过代理对象为您提供引用语义,同时Serializable为您提供值语义(即复制对象的状态)。

换句话说MarshalByRefValue,将允许您跨不同的 AppDomains 修改实例,而Serializable不会。当您只需要从一个 AppDomain 到另一个 AppDomain 获取信息时,后者很有用,例如从一个 AppDomain 到另一个 AppDomain 获取异常的内容。

于 2009-03-01T14:32:28.607 回答