当委托被传递到另一个 AppDomain 时,是否可以将委托编组为代理?

Tim*_*mwi 3 .net c# delegates appdomain marshalbyrefobject

不知何故,我假设传递给另一个 AppDomain 的委托会变成一个代理,就好像它是从MarshalByRefObject. 不幸的是,他们似乎没有。

假设在我的代码中我有一个MyClass这样的类:

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

[Serializable]
public sealed class Input { ... }
[Serializable]
public sealed class Output { ... }
Run Code Online (Sandbox Code Playgroud)

现在我需要将 的实例传递MyClass给另一个 AppDomain。

问题是存储在的委托SomeDelegate可能包含对几乎任何方法的引用,包括潜在的类型实例上的方法,它既不是[Serializable]也不是派生自MarshalByRefObject.

对于这个问题的缘故,让我们假设我不能改变的创建委托的代码,我也可以做MyClass一个MarshalByRefObject。然而,它是[Serializable]

(请注意,如果MyClass包含派生自 类型的字段MarshalByRefObject,则存储在该字段中的对象将变成代理,而类的其余部分将被序列化。)

有什么我可以做的事情可以让我将类作为序列化传递,但是委托变成了代理,就像它是一个MarshalByRefObject? (最好在 AppDomain 的设置中这样我就不需要更改MyClass,但也欢迎涉及更改类的建议,只要我不需要更改创建委托的代码。)

Tim*_*mwi 5

不幸的是,不能直接使委托本身成为代理。出于远程处理的目的,委托始终是按值对象。我发现一个奇怪的设计决定,因为我认为它违背了委托的逻辑语义,但这是另一回事。

为了解决这个问题,我必须将委托包装到一个我可以创建的类中,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以在委托的 setter 中实例化这个类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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是相当迂回的,但它满足了我的所有标准:代表仍然可以是任何东西;它将被远程调用(因为它将通过代理包装器);并且MyClass仍然[Serializable]代替代理。

从理论上讲,可以编写一个扩展方法Delegate.ToMarshalByRef(),该方法可以使用任何委托动态执行此操作,但它必须在运行时声明包装类,因为它需要Invoke具有正确签名的方法。