Можно ли упорядочивать делегатов как прокси, когда они передаются в другой домен приложения?

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

Скажем, в моем коде у меня есть класс MyClass вот так:

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

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

Теперь мне нужно передать экземпляр MyClass в другой домен приложения.

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

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

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

Есть ли что-то, что я могу сделать, что позволит мне передать класс как сериализованный, но с делегатом, превращенным в прокси, как если бы это был MarshalByRefObject? (Предпочтительно в настройке AppDomain, чтобы мне не нужно было менять MyClass, но предложения, связанные с изменением класса, также приветствуются, если мне не нужно менять код, создающий делегат.)


person Timwi    schedule 09.09.2013    source источник
comment
Теоретически вы можете создать оболочку AOP, которая на удаленном конце сериализует данные в домен приложения хостинга и запускает делегат. Немного сложно, хотя... Вы можете посмотреть DynamicProxy Виндзорского замка или Перехватчик 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