使用 MarshalByRefObject 的 [Serializable] 属性或子类?

Posted

技术标签:

【中文标题】使用 MarshalByRefObject 的 [Serializable] 属性或子类?【英文标题】:Use the [Serializable] attribute or subclassing from MarshalByRefObject? 【发布时间】:2010-10-10 15:03:56 【问题描述】:

我想跨 AppDomain 使用一个对象。

为此,我可以使用 [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());

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

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

【问题讨论】:

【参考方案1】:

使用 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 名称。

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

+1 链接到关于透明代理的迷人博客条目。那篇文章真的为我揭开了 MarshalByRefObject (MBRO) 的神秘面纱。现在我只是想知道 Context 和 ContextBoundObject 到底是什么:)【参考方案3】:

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

它们确实具有相同的效果。

使用MarshalByRefObject,您正在跨 AppDomain 边界引用一个对象。使用[Serializable] 正在制作对象的副本。如果在子域中修改了对象的状态然后再次检查(或在子 AppDomain 中执行 Console.WriteLine),则会显示此信息。

【讨论】:

好的...已经改变了问题。 似乎因为这两种方法具有相同的效果。【参考方案4】:

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

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

【讨论】:

请在投反对票时发表评论。谢谢。

以上是关于使用 MarshalByRefObject 的 [Serializable] 属性或子类?的主要内容,如果未能解决你的问题,请参考以下文章

MarshalByRefObject 生命周期

关于MarshalByRefObject的解释

MarshalByRefObject 是特殊的吗?

混合 MarshalByRefObject 和 Serializable

对“MarshalByRefObject”类型的引用声称它是在“System.Runtime”中定义的,但找不到

AppDomain 和 MarshalByRefObject 生命周期:如何避免 RemotingException?