c# - Delegate.CreateDelegate 无法绑定(bind)到静态泛型方法

标签 c# generics reflection static delegates

我正在尝试使用 Delegate.CreateDelegate [MSDN link]绑定(bind)到静态泛型方法,但绑定(bind)失败。 这是 PoC 代码:

public static class CreateDelegateTest {
    public static void Main() {
        Action actionMethod = CreateDelegateTest.GetActionDelegate();
        Action<int> intActionMethod = CreateDelegateTest.GetActionDelegate<int>();
        Func<int> intFunctionMethod = CreateDelegateTest.GetFunctionDelegate<int>();
    }

    public static Action GetActionDelegate() {
        return (Action)Delegate.CreateDelegate(typeof(Action), typeof(CreateDelegateTest), "ActionMethod");
    }

    public static Action<T> GetActionDelegate<T>() {
        return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), typeof(CreateDelegateTest), "GenericActionMethod");
    }

    public static Func<TResult> GetFunctionDelegate<TResult>() {
        return (Func<TResult>)Delegate.CreateDelegate(typeof(Func<TResult>), typeof(CreateDelegateTest), "GenericFunctionMethod");
    }

    public static void ActionMethod() { }

    public static void GenericActionMethod<T>(T arg) { }

    public static TResult GenericFunctionMethod<TResult>() {
        return default(TResult);
    }
}

actionMethod 已正确创建,但 intActionMethodintFunctionMethod 创建异常。

为什么 CreateDelegate 无法绑定(bind)到泛型方法?如何绑定(bind)到它们?

我已在 Microsoft Connect 上提交错误 [link] .如果您认为这是一个错误,请为它投票。

更新 2: 我错误地认为绑定(bind)到非函数泛型方法会成功。事实证明,任何泛型方法都无法绑定(bind)。

最佳答案

试试这个(我也无法让你的 GetActionDelegate() 工作):

public class CreateDelegateTest
{
    public static Func<TResult> GetFunctionDelegate<TResult>()
    {
        var methodInfo = typeof(CreateDelegateTest).GetMethod("FunctionMethod")
                                                   .MakeGenericMethod(typeof(TResult));
        return (Func<TResult>)Delegate.CreateDelegate(typeof(Func<TResult>), methodInfo);
    }

    public static Action<T> GetActionDelegate<T>()
    {
        var methodInfo = typeof(CreateDelegateTest).GetMethod("ActionMethod")
                                                   .MakeGenericMethod(typeof(T));
        return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), methodInfo);
    }
}

我不确定为什么 CreateDelegate(Type, Type, string)重载无法做到这一点,它只是以这种方式实现。

更新:

仍然可以对任何委托(delegate)类型使用相同的方法。主要思想是为 MakeGenericMethod() 找到正确的参数称呼。如何完成的快速示例:

public static Delegate CreateDelegate(Type delegateType, Type objectType, string methodName)
{
    var delegateMethod      = delegateType.GetMethod("Invoke");
    var delegateReturn      = delegateMethod.ReturnType;
    var delegateParameters  = delegateMethod.GetParameters();
    var methods             = objectType.GetMethods();
    MethodInfo method = null;
    ParameterInfo[] methodParameters = null;
    Type methodReturn = null;
    // find correct method by argument count
    foreach(var methodInfo in methods)
    {
        if(methodInfo.Name != methodName)
        {
            continue;
        }
        methodParameters = methodInfo.GetParameters();
        methodReturn = methodInfo.ReturnType;
        if(methodParameters.Length != delegateParameters.Length)
        {
            continue;
        }
        method = methodInfo;
    }
    if(method == null)
    {
        throw new Exception("Method not found");
    }
    if(method.IsGenericMethodDefinition)
    {
        var genericArguments    = method.GetGenericArguments();
        var genericParameters   = new Type[genericArguments.Length];

        int genericArgumentIndex = Array.IndexOf(genericArguments, methodReturn);
        if(genericArgumentIndex != -1)
        {
            genericParameters[genericArgumentIndex] = delegateReturn;
        }

        for(int i = 0; i < methodParameters.Length; ++i)
        {
            var methodParameter = methodParameters[i];
            genericArgumentIndex = Array.IndexOf(genericArguments, methodParameter.ParameterType);
            if(genericArgumentIndex == -1) continue;
            genericParameters[genericArgumentIndex] = delegateParameters[i].ParameterType;
        }

        if(Array.IndexOf(genericParameters, null) != -1)
        {
            throw new Exception("Failed to resolve some generic parameters.");
        }

        var concreteMethod = method.MakeGenericMethod(genericParameters);
        return Delegate.CreateDelegate(delegateType, concreteMethod);
    }
    else
    {
        return Delegate.CreateDelegate(delegateType, method);
    }
}

注意 1:我在这个例子中极度简化了重载方法解析 - 它仅依赖于参数计数。

注意2: 仍然可以用这种方式写一个不能被包裹在委托(delegate)中的方法,例如int Method<T>(string arg) (任何不在参数列表中引用通用参数或作为返回值的东西,无论如何这都是一种不好的做法)。

关于c# - Delegate.CreateDelegate 无法绑定(bind)到静态泛型方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14035204/

相关文章:

scala - 使用 scala 获取包中的所有类名

java - 查看对象是否是通过字符串传递的类的实例

c# - 可选参数 - 指定其中一个

c# - 为什么要调用基类?

c# - 如何从 Asp.NET Core 3.1 Startup 类访问 launchSettings.json 中找到的 `applicationUrl` 属性?

c# - 空测试与 try catch

C# 2.0 泛型 : How to create an Action object with zero parameters

java - 通过反射调用带有参数的方法

generics - 编写一个具有两种参数类型变体的泛型函数

c# - 一般执法