我有以下类型层次结构:
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);
}
感谢任何有助于我实现目标的想法或替代方案。
答案 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");