Смешивание MarshalByRefObject и Serializable

Различные источники объясняют, что

Когда объект является производным от MarshalByRefObject, ссылка на объект будет передаваться из одного домена приложения в другой, а не на сам объект. Когда объект помечен как [Serializable], объект будет автоматически сериализован, перенесен из одного домена приложения в другой, а затем десериализован для создания точной копии объекта во втором домене приложения. Обратите внимание, что пока MarshalByRefObject передает ссылку, [Serializable] вызывает копирование объекта. [источник]

Я разрабатываю свое первое приложение, использующее домены приложений, и мне интересно, что происходит, когда вы помещаете ссылки на MarshalByRefObjects внутри сериализуемых объектов, которые не реализуют MarshalByRefObject, потому что пока я не могу найти никакой документации по этому вопросу.

Например, что произойдет, если я попытаюсь вернуть List<MBR> где MBR : MarshalByRefObject через границу домена приложения? Могу ли я получить копию List<MBR>, где каждый MBR является TransparentProxy исходным объектом? И есть ли документация о технических деталях смешивания двух механизмов?


person Qwertie    schedule 07.01.2012    source источник


Ответы (2)


Я только что провел быстрый тест с List<MBR>, и, похоже, он работает так, как я надеялся:

public class MBR : MarshalByRefObject
{
    List<MBR> _list;
    public MBR() { _list = new List<MBR> { this }; }
    public IList<MBR> Test() { return _list; }
    public int X { get; set; }
}

// Later...
var mbr = AppDomainStarter.Start<MBR>(@"C:\Program Files", "test", null, true);
var list = mbr.Test();
list[0].X = 42;
list.Clear();
Debug.WriteLine(string.Format("X={0}, Count={1}", mbr.X, mbr.Test().Count));

Результат - X=42, Count=1, и отладчик показывает, что List<MBR> содержит __TransparentProxy. Итак, очевидно, что MarshalByRefObject успешно маршалируется по ссылке внутри другого объекта, который был маршалирован по значению.

Я все еще хотел бы увидеть документацию или технические подробности, если кто-нибудь сможет их найти.

Для всех, кому интересно, я написал эту удобную песочницу AppDomainStarter:

/// <summary><see cref="AppDomainStarter.Start"/> starts an AppDomain.</summary>
public static class AppDomainStarter
{
    /// <summary>Creates a type in a new sandbox-friendly AppDomain.</summary>
    /// <typeparam name="T">A trusted type derived MarshalByRefObject to create 
    /// in the new AppDomain. The constructor of this type must catch any 
    /// untrusted exceptions so that no untrusted exception can escape the new 
    /// AppDomain.</typeparam>
    /// <param name="baseFolder">Value to use for AppDomainSetup.ApplicationBase.
    /// The AppDomain will be able to use any assemblies in this folder.</param>
    /// <param name="appDomainName">A friendly name for the AppDomain. MSDN
    /// does not state whether or not the name must be unique.</param>
    /// <param name="constructorArgs">Arguments to send to the constructor of T,
    /// or null to call the default constructor. Do not send arguments of 
    /// untrusted types this way.</param>
    /// <param name="partialTrust">Whether the new AppDomain should run in 
    /// partial-trust mode.</param>
    /// <returns>A remote proxy to an instance of type T. You can call methods 
    /// of T and the calls will be marshalled across the AppDomain boundary.</returns>
    public static T Start<T>(string baseFolder, string appDomainName, 
        object[] constructorArgs, bool partialTrust)
        where T : MarshalByRefObject
    {
        // With help from http://msdn.microsoft.com/en-us/magazine/cc163701.aspx
        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationBase = baseFolder;

        AppDomain newDomain;
        if (partialTrust) {
            var permSet = new PermissionSet(PermissionState.None);
            permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            permSet.AddPermission(new UIPermission(PermissionState.Unrestricted));
            newDomain = AppDomain.CreateDomain(appDomainName, null, setup, permSet);
        } else {
            newDomain = AppDomain.CreateDomain(appDomainName, null, setup);
        }
        return (T)Activator.CreateInstanceFrom(newDomain, 
            typeof(T).Assembly.ManifestModule.FullyQualifiedName, 
            typeof(T).FullName, false,
            0, null, constructorArgs, null, null).Unwrap();
    }
}
person Qwertie    schedule 08.01.2012

Насколько я понимаю, MBR может быть только переданный объект верхнего уровня. В вашем сценарии, поскольку List не является MBR, при переходе через границу вы получите сериализованные копии.

В этом разделе документации MSDN объясняется это поведение:

MarshalByRefObject - это базовый класс для объектов, которые взаимодействуют через границы домена приложения, обмениваясь сообщениями с помощью прокси. Объекты, которые не наследуются от MarshalByRefObject, неявно маршалируются по значению. Когда удаленное приложение ссылается на объект упорядочивания по значению, копия объекта передается через границы домена приложения.

Итак, поскольку переданный класс (List) не является MBR, он будет сериализован вместе с его содержимым.

Кроме того, хотя это не относится к вопросу напрямую, очень важно отметить следующее поведение:

... члены объекта не могут использоваться за пределами домена приложения, в котором они были созданы.

person competent_tech    schedule 07.01.2012
comment
Итак, если MBR не поддерживает сериализацию, возникнет ли какое-то исключение, когда .NET Framework попытается сериализовать его? А как насчет возврата массивов MBR []? System.Array является сериализуемым, а не производным от MarshalByRefObject. - person Qwertie; 08.01.2012
comment
re не сериализуем: да, во время выполнения произойдет исключение. re Массивы MBR: у вас та же проблема, что и у List, поскольку массив является классом верхнего уровня. - person competent_tech; 08.01.2012
comment
Эксперимент показывает, что несериализуемая MBR внутри сериализуемого контейнера без исключения передается по ссылке. - person Ivan Krivyakov; 31.07.2013