4

Delegate.CreateDelegate [MSDNリンク]を使用して静的ジェネリックメソッドにバインドしようとしていますが、バインドに失敗します。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適切に作成されますが、intActionMethodとのintFunctionMethod作成はスローされます。

CreateDelegateジェネリックメソッドにバインドできないのはなぜですか?それらにバインドする方法は?

MicrosoftConnect [リンク]でバグを送信しました。これがバグだと思われる場合は、投票してください。

更新2:非関数ジェネリックメソッドへのバインドが成功するとは間違っていました。ジェネリックメソッドはバインドに失敗することが判明しました。

4

1 に答える 1

3

これを試してください(あなたのバージョン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)。この方法で実装されているだけです。

アップデート:

どのデリゲートタイプでも同じアプローチを使用できます。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:たとえば、この方法でデリゲートにラップできないメソッドを作成することは可能ですint Method<T>(string arg)(引数リスト内のジェネリック引数を参照しないものや、戻り値として参照しないものは、いずれにしても悪い習慣です)。

于 2012-12-26T02:19:18.983 に答える