【问题标题】:Use the [Serializable] attribute or subclassing from MarshalByRefObject?使用 MarshalByRefObject 的 [Serializable] 属性或子类?
【发布时间】: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());

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

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

【问题讨论】:

    标签: c# remoting appdomain


    【解决方案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 获取异常的内容。

      【讨论】:

      • 请在投反对票时发表评论。谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      • 2015-07-09
      • 2015-12-10
      相关资源
      最近更新 更多