web-dev-qa-db-ja.com

メソッドのシグネチャが事前にわからない場合に、MethodInfoからデリゲートを作成する方法は?

任意のシグネチャを持つ非ジェネリックな静的メソッドを表すMethodInfoインスタンスを受け取り、後で_Delegate.DynamicInvoke_メソッドを使用して呼び出すことができるそのメソッドにバインドされたデリゲートを返すメソッドが必要です。私の最初のナイーブな試みはこのように見えました:

_using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        var method = CreateDelegate(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)}));
        method.DynamicInvoke("Hello world");
    }

    static Delegate CreateDelegate(MethodInfo method)
    {
        if (method == null)
        {
            throw new ArgumentNullException("method");
        }

        if (!method.IsStatic)
        {
            throw new ArgumentNullException("method", "The provided method is not static.");
        }

        if (method.ContainsGenericParameters)
        {
            throw new ArgumentException("The provided method contains unassigned generic type parameters.");
        }

        return method.CreateDelegate(typeof(Delegate)); // This does not work: System.ArgumentException: Type must derive from Delegate.
    }
}
_

_MethodInfo.CreateDelegate_メソッドが正しいデリゲート型自体を把握できることを望みました。まあ、明らかにできません。では、提供されたMethodInfoインスタンスに一致する署名を持つデリゲートを表す_System.Type_のインスタンスを作成するにはどうすればよいですか?

40

System.Linq.Expressions.Expression.GetDelegateType メソッドを使用できます:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

class Program
{
    static void Main()
    {
        var writeLine = CreateDelegate(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));
        writeLine.DynamicInvoke("Hello world");

        var readLine = CreateDelegate(typeof(Console).GetMethod("ReadLine", Type.EmptyTypes));
        writeLine.DynamicInvoke(readLine.DynamicInvoke());
    }

    static Delegate CreateDelegate(MethodInfo method)
    {
        if (method == null)
        {
            throw new ArgumentNullException("method");
        }

        if (!method.IsStatic)
        {
            throw new ArgumentException("The provided method must be static.", "method");
        }

        if (method.IsGenericMethod)
        {
            throw new ArgumentException("The provided method must not be generic.", "method");
        }

        return method.CreateDelegate(Expression.GetDelegateType(
            (from parameter in method.GetParameters() select parameter.ParameterType)
            .Concat(new[] { method.ReturnType })
            .ToArray()));
    }
}

おそらく!method.IsStaticの2回目のチェックでコピーアンドペーストエラーが発生します。ArgumentNullExceptionは使用しないでください。そして、ArgumentExceptionへの引数としてパラメーター名を提供するのは良いスタイルです。

すべてのジェネリックメソッドを拒否する場合はmethod.IsGenericMethodを使用し、非置換型パラメーターを持つジェネリックメソッドのみを拒否する場合はmethod.ContainsGenericParametersを使用します。

35
Oksana Gimmel

System.LinQ.Expressionsを試してみてください。

...
using System.Linq.Expressions;
...

static Delegate CreateMethod(MethodInfo method)
{
    if (method == null)
    {
        throw new ArgumentNullException("method");
    }

    if (!method.IsStatic)
    {
        throw new ArgumentException("The provided method must be static.", "method");
    }

    if (method.IsGenericMethod)
    {
        throw new ArgumentException("The provided method must not be generic.", "method");
    }

    var parameters = method.GetParameters()
                           .Select(p => Expression.Parameter(p.ParameterType, p.Name))
                           .ToArray();
    var call = Expression.Call(null, method, parameters);
    return Expression.Lambda(call, parameters).Compile();
}

後でそれを次のように使用します

var method = CreateMethod(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)}));
method.DynamicInvoke("Test Test");
3
Khoa Nguyen