Использовать атрибут [Serializable] или наследовать от MarshalByRefObject?

Я хочу использовать объект в доменах приложений.

Для этого я могу использовать атрибут [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)

Решение

Использование MarshallByRef выполнит ваши методы в удаленном домене приложений. Когда вы используете CreateInstanceAndUnwrap с объектом Serializable, копия объекта создается в локальном домене приложений, поэтому любой вызов метода будет выполняться в локальном домене приложений.

Если вы хотите общаться между доменами приложений, используйте подход 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());

    }
}

Этот код будет отображать «OtherAppDomain» при вызове WhatIsMyAppDomain из объекта MarshallByRef и ваше имя AppDomain по умолчанию при вызове из объекта Serializable.

Почему оба подхода имеют одинаковый эффект?

Они не имеют такого же эффекта.

С MarshalByRefObject вы ссылаетесь на один объект через границы домена приложений. С [Serializable] копии объекта делается. Это будет отображаться, если состояние объекта будет изменено в дочернем домене, а затем снова проверено (или выполнено Console.WriteLine внутри дочернего домена приложения ).

MarshalByRefValue и Serializable реализовать другую семантику для удаленного взаимодействия / связи между доменами приложений. MarshalByRefValue по существу дает вам ссылочную семантику через прокси-объект, в то время как Serializable дает вам семантику значений (т. е. состояние объекта копируется).

Другими словами MarshalByRefValue, вы сможете изменять экземпляр в разных доменах приложений, а Serializable не - нет. Последнее полезно, когда вам просто нужно получить информацию из одного домена приложения в другой, например, чтобы получить содержимое исключения из одного домена приложения в другой.

Эти подходы имеют совершенно разные эффекты.

С версией MarshalByRef вы создаете 1 экземпляр вашего объекта. Он будет жить во вновь созданном домене приложений. Весь доступ к объекту осуществляется через TransparentProxy .

В версии Serializable вы создаете 2 экземпляра вашего объекта. Один создается во вновь созданном домене приложений. Затем вызов CreateInstanceAndUnwrap сериализует этот объект и десериализует его в исходном домене приложения. Это создает вторую версию объекта, полностью независимую от первой. Фактически, следующий сборщик мусора почти наверняка удалит исходный объект, и у вас останется один экземпляр.