C#在注入第一个参数的给定方法上创建lambda

Pio*_*trK 13 c# reflection lambda

在 C# 中,我在给定的类(非静态)中定义了以下方法:

int MyMethod(ScriptEngine script, int a, int b) {
    return a + b;
}

void MyMethod2(ScriptEngine script, string c) {
    // do something with c
}
Run Code Online (Sandbox Code Playgroud)

我想创建包装 lambda / Action / Delegate / MethodInfo(所有这些都被脚本引擎接受),它会自动传递ScriptEnginethis从给定的预定义变量。

到目前为止,我已经尝试过:

// With overloads up to 16 template parameters
Action<T1> Wrap<T1>(Action<ScriptEngine, T1> func, ScriptEngine script) { 
     return (Action<T1>) ((t1) => func(script, t1));
}
Run Code Online (Sandbox Code Playgroud)

但是当调用 MyMethod2 时,我得到了The type arguments for method … cannot be inferred from the usage. Try specifying the type arguments explicitly. 如果我明确指定模板参数,它就可以工作,但我想避免这样的规范。

有没有其他方法(不需要按照我的解决方案)我可以自动(或半自动)创建这样的包装器?

值得一提的是,有一个专用的抽象方法void RegisterAll(ScriptEngine script)可以注册给定子类的所需成员。

这是我试图实现的一个例子:

class ScriptEngine { // Stub to have complete example, actual implementation is defined elsewhere
    void RegisterApi(string name, MethodInfo methodInfo) { }
    void RegisterApi(string name, Delegate delegateSelf) { }
}

class Api {
    int MyMethod(ScriptEngine script, int a, int b) {
        return a + b;
    }

    void MyMethod2(ScriptEngine script, string c) {
        // do something with c
    }

    void RegisterAll(ScriptEngine script) {
         // Is there any way to shorten this up (not providing MyMethod twice, not providing template arguments?)
        script.RegisterApi(nameof(MyMethod), (Delegate)Wrap<string>(MyMethod, script));
    }

}
Run Code Online (Sandbox Code Playgroud)

问题是如何改进这种RegisterApi方法,以便它:

  • 只采取一次方法
  • 不需要通过模板方法指定参数

Pio*_*trK 2

实际上还有另一种解决方案,不涉及发出新的表达式(在 iOS 上可能会失败!)

首先,让我们定义以下包装器:

    private class Wrapper
    {
        public readonly object container;
        public readonly MethodInfo method;
        public readonly ScriptEngine engine;

        public Wrapper(object container, MethodInfo method, ScriptEngine engine)
        {
            this.container = container;
            this.method = method;
            this.engine = engine;
        }

        public Action CreateAction()
        {
            return () => method.Invoke(container, new object[] { engine });
        }
        public Action<T1> CreateAction<T1>()
        {
            return (arg1) => method.Invoke(container, new object[] { engine, arg1 });
        }
        // etc
    }
Run Code Online (Sandbox Code Playgroud)

现在你可以这样注册方法:

        var type = typeof(Wrapper);
        var instance = Activator.CreateInstance(type, new object[] { container, methodInfo, engine });
        MethodInfo methodActual = null;
        if (methodInfo.ReturnType == typeof(void))
        {
            var methods = type.GetMethods().Where(x => x.Name == "CreateAction");

            foreach (var method in methods)
            {
                if (method.GetGenericArguments().Length == methodInfo.GetParameters().Length - 1)
                {
                    methodActual = method.MakeGenericMethod(methodInfo.GetParameters().Skip(1).Select(x => x.ParameterType).ToArray());
                }
            }
        }
        var actionToRegister = methodActual.Invoke(instance, new object[0]);
Run Code Online (Sandbox Code Playgroud)