Възможно ли е делегатите да бъдат маршалирани като прокси сървъри, когато се предават на друг AppDomain?

По някакъв начин предположих, че делегатите, предадени на друг AppDomain, ще се превърнат в прокси, сякаш е обект, извлечен от MarshalByRefObject. За съжаление, изглежда, че не го правят.

Да кажем, че в моя код имам клас MyClass като този:

[Serializable]
public sealed class MyClass
{
    public Func<Input, Output> SomeDelegate;
}

[Serializable]
public sealed class Input { ... }
[Serializable]
public sealed class Output { ... }

Сега трябва да предам екземпляр на MyClass към друг AppDomain.

Проблемът е, че делегатът, съхранен в SomeDelegate, може да съдържа препратка към почти всеки метод, включително потенциално метод на екземпляр от тип, който не е нито [Serializable], нито е извлечен от MarshalByRefObject.

В името на този въпрос, нека приемем, че не мога да променя кода, който създава делегата, нито мога да направя MyClass MarshalByRefObject. Това обаче е [Serializable].

(Имайте предвид, че ако MyClass съдържа поле от тип, който произлиза от MarshalByRefObject, обектът, съхранен в това поле, ще бъде превърнат в прокси, докато останалата част от класа е сериализирана.)

Има ли нещо, което мога да направя, което ще ми позволи да предам класа като сериализиран, но с делегата, превърнат в прокси, точно както би било, ако беше MarshalByRefObject? (За предпочитане в настройката на AppDomain, така че да не е необходимо да променям MyClass, но предложенията, които включват промяна на класа, също са добре дошли, стига да не е необходимо да променям кода, който създава делегата.)


person Timwi    schedule 09.09.2013    source източник
comment
На теория бихте могли да създадете AOP обвивка, която от отдалечения край сериализира данните в хостинг AppDomain и да изпълни делегата. Малко сложно обаче... Можете да погледнете DynamicProxy на Windsor Castle или Interceptor на Unity, за да направите това...   -  person Aron    schedule 09.09.2013


Отговори (1)


За съжаление не е възможно директно да направите самия делегат прокси. Делегатите винаги са обекти по стойност за целта на отдалеченото. Намирам това за странно дизайнерско решение, тъй като мисля, че противоречи на логическата семантика на делегатите, но това е друг въпрос.

За да реша това, трябваше да обвия делегата в клас, който мога да направя MarshalByRefObject, така че да бъде проксиран. Този клас трябва да има метод, който е еквивалентен на извикване на делегата. За да запазя това чисто, реших да направя този клас частен:

private sealed class myDelegateWrapper : MarshalByRefObject
{
    public Output Invoke(Input input)
    {
        return _delegate(input);
    }

    private Func<Input, Output> _delegate;

    public myDelegateWrapper(Func<Input, Output> dlgt)
    {
        _delegate = dlgt;
    }
}

Сега мога да инстанцирам този клас в сетера на делегата в MyClass:

[Serializable]
public sealed class MyClass
{
    private Func<Input, Output> _someDelegate;

    public Func<Input, Output> SomeDelegate
    {
        get
        {
            return _someDelegate;
        }
        set
        {
            if (value == null)
                _someDelegate = null;
            else
                _someDelegate = new myDelegateWrapper(value).Invoke;
        }
    }
}

Това е доста заобиколно, но отговаря на всичките ми критерии: делегатът все още може да бъде всичко; ще бъде извикан дистанционно (защото ще премине през прокси обвивката); и MyClass все още е [Serializable] вместо прокси.

На теория може да се напише метод за разширение Delegate.ToMarshalByRef(), който ще направи това динамично с всеки делегат, но ще трябва да декларира обвиващия клас по време на изпълнение, тъй като се нуждае от Invoke метод с правилния подпис.

person Timwi    schedule 10.09.2013