将运行时生成的委托包装在工厂中,并将调用结果作为参数传递

本文关键字:调用 结果 参数传递 运行时 包装 工厂 | 更新日期: 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.RegisterFunctionfuncDelegate需要有效地执行与我们的通用扩展相同的操作,即(x, y) => factory().Execute(x, y)

不幸的是,这就是我的反射技能失败的地方。

将运行时生成的委托包装在工厂中,并将调用结果作为参数传递

创建一个泛型类来存储泛型委托,而不是使用泛型方法。

你可以把对泛型方法的调用委托给你的泛型类型的方法。