web-dev-qa-db-ja.com

メソッド参照からmethodinfoを取得するC#

指定した型のTypeインスタンスを取得する場合は、C#typeofキーワードを使用できます。しかし、参照によってメソッドのMethodInfoを取得したい場合、何を使用できますか?

たとえば、シンプルなコンソールアプリがあります。 _Program.Main_メソッドが含まれています。 methodinfoof(Program.Main)のようなものを使用してMethodInfoを取得したい。メソッド名が変更される可能性があるため、この問題があります。そのため、Type.GetMethodInfo(string MethodName)だけを使用することはできません。

MethodInfoを取得したい約10,000のメソッドがあるので、カスタム属性などをメソッドに追加することは解決策ではありません。

22

以前に投稿された回答を少し変更しましたが、このブログ投稿はあなたが求めていることを達成しているようです。 http://blog.functionalfun.net/2009/10/getting-methodinfo-of-generic-method.html

使用例は次のとおりです。

var methodInfo = SymbolExtensions.GetMethodInfo(() => Program.Main());

元の答えはこの質問に対するものでした。 https://stackoverflow.com/a/9132588/5827

非静的メソッドには式ツリーを使用できます。ここに例があります。

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

public static class MethodInfoHelper
{
    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var member = expression.Body as MethodCallExpression;

        if (member != null)
            return member.Method;

        throw new ArgumentException("Expression is not a method", "expression");
    }
}

次のように使用します。

        MethodInfo mi = MethodInfoHelper.GetMethodInfo<Program>(x => x.Test());
        Console.WriteLine(mi.Name);

Test()は、Programクラスで宣言されたメンバー関数です。

プロパティのゲッターとセッターをサポートしたい場合は、代わりにMemberExpressionMemberInfoを使用してください。

12
Dmitry S.

テストクラス

public class  Foo
{
    public void DoFoo()
    {
        Trace.WriteLine("DoFoo");
    }

    public static void DoStaticFoo()
    {
        Trace.WriteLine("DoStaticFoo");
    }
}

そして、あなたはこのようなことをすることができます

MethodInfo GetMethodInfo(Action a)
{
    return a.Method;
}

var foo = new Foo();
MethodInfo mi = GetMethodInfo(foo.DoFoo);
MethodInfo miStatic = GetMethodInfo(Foo.DoStaticFoo);

//do whatever you need with method info

更新
メソッドにいくつかのパラメータがある場合、@ Gregコメントごとに、Action<T>Action<T1, T2>Action<T1, T2, T3>、またはFunc<T1>を使用できます。不便なのは、 GetMethodInfoのオーバーロードを書き込む必要があります。

6
oleksii

私はこれが非常に古い投稿であることを知っていますが、これに対する簡単な解決策をまだ探している人のために捨てるつもりです。

_typeof(Program).GetMethods();
_

属性やパラメータの有無に関係なく、ProgramクラスのすべてのメソッドのMethodInfoを含む配列を返します。

たとえば、すべての10.000+メソッドの名前を一覧表示したい場合は、それを繰り返すことができます。

メソッド名が変更された場合、このようにtypeof(Program).GetMethod(nameof(Program.Main));を実行することもできます。VisualStudioのリファクタリングによって、ここでも名前が変更されます。

注:「nameof」キーワードは、質問が投稿された5年前には利用できませんでした。

5
Matheus Rocha

ここで手元の問題にいくつかの説明を付け加えましょう。指定されたメソッドに関する情報を返すメソッドGetMethodInfo(SomeMethodSymbol)を探しています。メソッドはC#でオーバーロードされる可能性があるため、これは簡単ではありません。したがって、基本的には、追加の手がかりを呼び出しに提供して、コンパイラー(およびIntellisenseなどの他のコードアナライザー)がどのメソッドについて話しているのかを理解する必要があります。

たとえば、Math.Absメソッドに関する情報を探しているとします。次に、探しているメソッドのオーバーロードされたバージョンを正確に指定する必要があります。

// int
MethodInfo info1 = ((Func<int, int>)Math.Abs).Method;

// or double ?
MethodInfo info2 = ((Func<double, double>)Math.Abs).Method;

Math.Expメソッドのように既存のオーバーロードが1つしかない場合でも、メソッドが将来オーバーロードされてコードがコンパイルされなくなるため、タイピングの手がかりを提供する必要があります。

直接ヘルパー

上記の発言を踏まえて、次の一連のヘルパーメソッドを提供して、すべてのメソッドをキャストしてその情報に到達するという面倒なタスクを多少軽減できます。

public static class GetMethodInfoUtil
{
    // No cast necessary
    public static MethodInfo GetMethodInfo(Action action) => action.Method;
    public static MethodInfo GetMethodInfo<T>(Action<T> action) => action.Method;
    public static MethodInfo GetMethodInfo<T,U>(Action<T,U> action) => action.Method;
    public static MethodInfo GetMethodInfo<TResult>(Func<TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, TResult>(Func<T, TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, U, TResult>(Func<T, U, TResult> fun) => fun.Method;

    // Cast necessary
    public static MethodInfo GetMethodInfo(Delegate del) => del.Method;
}

次に、これらのヘルパーを次のように使用します。

var methodInfos = new[] {

    // Static methods
    GetMethodInfo<int, int>(Math.Abs),
    GetMethodInfo<double, double>(Math.Abs),
    GetMethodInfo<long, long, long>(Math.Max),

    // Static void methods
    GetMethodInfo(Console.Clear),
    GetMethodInfo<string[]>(Main),

    // With explicit cast if too many arguments
    GetMethodInfo((Action<string, object, object>)Console.WriteLine),

    // Instance methods
    GetMethodInfo<string, bool>("".StartsWith),
    GetMethodInfo(new List<int>().Clear),
};

Console.Clearのような引数を取らないvoid静的メソッドを除いて、型情報は引き続き提供される必要があることに注意してください。また、インスタンスメソッドの場合、実際のインスタンスを使用して、より多くのリソースを使用する適切なメソッドを取得する必要があります。

間接ヘルパー

さて、いくつかのコーナーケースでは、上記のヘルパーは機能しません。たとえば、メソッドがoutパラメータを使用するとします。これらの特殊なケースでは、ラムダ式からメソッド情報を抽出するのが便利になり、他のポスターによって提供されるソリューションに戻ります(コードインスピレーション here ):

public static class GetIndirectMethodInfoUtil
{
    // Get MethodInfo from Lambda expressions
    public static MethodInfo GetIndirectMethodInfo(Expression<Action> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T>(Expression<Action<T>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);

    // Used by the above
    private static MethodInfo GetIndirectMethodInfo(LambdaExpression expression)
    {
        if (!(expression.Body is MethodCallExpression methodCall))
        {
            throw new ArgumentException(
                $"Invalid Expression ({expression.Body}). Expression should consist of a method call only.");
        }
        return methodCall.Method;
    }
}

あなたはこのようなものを使うでしょう:

int dummyInt;
var moreMethodInfos = new[]
{
    // Extracted from lambdas
    GetIndirectMethodInfo(() => "".StartsWith("")),
    GetIndirectMethodInfo((string s) => s.StartsWith(s)),
    GetIndirectMethodInfo(() => int.TryParse("", out dummyInt)),
};

型情報は引き続き引数型から間接的に提供されることに注意してください。また、outパラメータを使用できるようにするためだけに、ダミー引数が追加されていることに注意してください。

完全なデモプログラム: https://dotnetfiddle.net/CkS075

2
Frederic

多分理想的な方法ではないかもしれませんが、それは役立つかもしれません:

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));
2
Mladen B.

これを支援するために必要なヘルパー関数を作成するT4テンプレートを作成しました。 Func <>またはAction <>メソッドからMethodInfoオブジェクトを取得する関数のリストを作成します。

次のコードをGetMethodInfo.ttという名前のファイルにコピーします。

<#@ template language="C#" #>
<#@ output extension=".cs" encoding="utf-8" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Tools
{
    public static class GetMethodInfo
    {
<# int max = 12;
for(int i = 0; i <= max; i++) 
{
    var builder = new StringBuilder();

    for(int j = 0; j <= i; j++) 
    {
        builder.Append("T");
        builder.Append(j);
        if(j != i) 
        {
            builder.Append(", ");
        }
    }

    var T = builder.ToString();
#>
        public static MethodInfo ForFunc<T, <#= T #>>(Expression<Func<T, <#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

        public static MethodInfo ForAction<<#= T #>>(Expression<Action<<#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

<# } #>
    }
}

メモ

  • .ttテンプレートのビルドアクションなしに設定されていることを確認してください
  • max変数を適切な設定に設定することで、多かれ少なかれ関数を作成できます。
1
MovGP0