如何使C#Create.Delegate支持继承?

时间:2016-12-02 11:36:53

标签: c# .net reflection delegates

我有以下类型层次结构:

public abstract class Parent { }
public class Child : Parent
{
    public Task SayAsync(string arg)
    {
        Console.WriteLine(arg);
        return Task.CompletedTask;
    }    
}

我需要实现以下目标:

  • 在运行时创建Parent的任何实例(这已通过调用我在代码中的其他地方获得的Func<Parent>来解决)
  • 然后调用(在该实例上)所有public方法(总是返回Task并接受string作为参数)传入arg不是一个常数。

以上存在于热路径中,因此为了提高性能,我使用Cached Delegates所以在启动时我将创建委托,然后我将在需要时缓存和使用。

这是我为Child明确做的一个例子,但我无法弄清楚如何让委托接受Parent(因为我在编译时不知道类型)。

// If I change Child to Parent, I get "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type"
private delegate Task Invoker(Child instance, string arg);

void Main()
{
    var instance = new Child(); // This will be obtained by calling a Func<Parent>
    var methodWithArg = instance.GetType().GetMethod("SayAsync");

    var func = GetDelegateWithArg(methodWithArg);

    func(instance, "Foo");
}

private static Invoker GetDelegateWithArg(MethodInfo method)
{
    object pointer = null;
    return (Invoker)Delegate.CreateDelegate(typeof(Invoker), pointer, method);
}

感谢任何有助于我实现目标的想法或替代方案。

2 个答案:

答案 0 :(得分:2)

您可以尝试使用表达式树生成委托:

public abstract class Parent { }

public class Child : Parent
{
    public Task SayAsync(string arg)
    {
        Console.WriteLine(arg);
        return Task.CompletedTask;
    }
}

public delegate Task Invoker(Parent instance, string arg);

class Program
{
    static void Main(string[] args)
    {
        Parent instance = new Child(); // This will be obtained by calling a Func<Parent>
        var methodWithArg = instance.GetType().GetMethod("SayAsync");

        var func = GetDelegateWithArg(methodWithArg);

        func(instance, "Foo");
    }

    private static Invoker GetDelegateWithArg(MethodInfo method)
    {
        var instanceParameter = Expression.Parameter(typeof(Parent));
        var argParameter = Expression.Parameter(typeof(string));
        var body = Expression.Call(
            Expression.Convert(instanceParameter, method.DeclaringType),
            method,
            argParameter);
        var lambda = Expression.Lambda<Invoker>(body, instanceParameter, argParameter);
        return lambda.Compile();
    }
}

生成的委托仍然非常快,但可能比Delegate.CreateDelegate()方法创建的要慢一点。

答案 1 :(得分:1)

可能尝试将目标实例传递给GetDelegateWithArg方法并更改Invoker的签名以仅接收参数。像这样:

private delegate Task Invoker(string arg);

private static Invoker GetDelegateWithArg(MethodInfo method, object target)
{            
    return (Invoker)Delegate.CreateDelegate(typeof(Invoker), target, method);
}

建议更改后,您可以将实例传递给委托创建者并使用一个参数来启用func:

func("Foo");