将运行时生成的委托包装在工厂中,并将调用结果作为参数传递
本文关键字:调用 结果 参数传递 运行时 包装 工厂 | 更新日期: 2023-09-27 18:14:40
希望下面的例子能比标题更好地解释问题:/
假设我们有一个允许注册不同函数的Calculator
类:
public class Calculator
{
public void RegisterFunction(string functionName, Func<int, int, int> function)
{
functions.Add(functionName, function);
}
public int Calculate(string functionName, int x, int y)
{
Func<int, int, int> function;
if (functions.TryGetValue(functionName, out function))
{
return function(x, y);
}
throw new ArgumentException("No function is registered with name " + functionName);
}
}
我们可以通过提供一个内联委托来注册一个"加法"函数:
calculator.RegisterFunction("add", (x, y) => x + y);
Console.WriteLine(calculator.Calculate("add", 2, 3));
然而,为了使计算器更容易扩展并支持复杂函数,我们提供了一个与内联委托具有相同签名的接口:
public interface ICalculatorFunction
{
int Execute(int x, int y);
}
我们可以为Calculator
创建一个扩展方法,使注册ICalculatorFunction
实例变得容易:
public static void RegisterFunction<TFunction>(this Calculator calculator, TFunction function) where TFunction : ICalculatorFunction
{
calculator.RegisterFunction(typeof(TFunction).Name, function.Execute);
}
注册:calculator.RegisterFunction(new Multiply());
然而,这里我们传递的是一个实例,实际上我们只希望在需要执行计算时才创建函数实例。我们可以通过传入一个"函数工厂"来支持这一点:
public static void RegisterFunction<TFunction>(this Calculator calculator, Func<TFunction> factory) where TFunction : ICalculatorFunction
{
calculator.RegisterFunction(typeof(TFunction).Name, (x, y) => factory().Execute(x, y));
}
这里我们提供了一个内联委托,它首先调用工厂,然后在创建的ICalculatorFunction
实例上调用Execute
。
calculator.RegisterFunction(() => new Subtract());
问题当函数类型在编译时已知时,上述所有示例都可以正常工作。然而,我们需要支持传入计算器函数Type
,并在Calculator
实例上调用相同的Register
方法。
到目前为止,我只能提前实现创建ICalculatorFunction
实例,使用Delegate.CreateDelegate
然后将对Execute
函数的引用传递给Calculator
:
public static void RegisterFunction(this Calculator calculator, Type functionType, Func<Type, object> factory)
{
var delegateType = typeof(Func<,,>).MakeGenericType(typeof(int), typeof(int), typeof(int));
var funcDelegate = (Func<int, int, int>)Delegate.CreateDelegate(delegateType, factory(functionType), "Execute");
var registerMethod = calculator.GetType().GetMethod("RegisterFunction", BindingFlags.Instance | BindingFlags.Public);
registerMethod.Invoke(calculator, new object[] { functionType.Name, funcDelegate });
}
注册:calculator.RegisterFunction(typeof(Subtract), type => Activator.CreateInstance(type));
然而,我传递给Calculator.RegisterFunction
的funcDelegate
需要有效地执行与我们的通用扩展相同的操作,即(x, y) => factory().Execute(x, y)
。
不幸的是,这就是我的反射技能失败的地方。
创建一个泛型类来存储泛型委托,而不是使用泛型方法。
你可以把对泛型方法的调用委托给你的泛型类型的方法。