簡體   English   中英

從方法引用 C# 中獲取方法信息

[英]get methodinfo from a method reference C#

當我們想要獲取指定類型的 Type 實例時,我們可以使用 C# typeof關鍵字。 但是如果我想通過它的引用獲取方法的MethodInfo ,我可以使用什么?

例如,我有一個簡單的控制台應用程序。 它包含Program.Main方法。 我想通過使用諸如methodinfoof(Program.Main)類的東西來獲取MethodInfo 我有這個問題,因為方法名稱可能會改變,所以我不能只使用Type.GetMethodInfo(string MethodName)

我有大約 10 000 個方法想要獲取MethodInfo ,因此向我的方法添加任何自定義屬性或其他任何內容都不是解決方案。

對先前發布的答案稍作改編,但這篇博文似乎實現了您的要求; 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 類中聲明的成員函數。

如果您想支持屬性 getter 和 setter,請改用MemberExpressionMemberInfo

我知道這是一篇很老的帖子,但我會把它扔給那些可能仍在尋找簡單解決方案的人。 似乎沒有人想到最簡單的解決方案:

typeof(Program).GetMethods();

返回一個包含 Program 類中所有方法的 MethodInfo 的數組,而不管屬性或是否有參數。

例如,如果您想列出所有 10.000 多個方法的名稱,您可以對其進行迭代。

你也可以做typeof(Program).GetMethod(nameof(Program.Main)); 這樣,如果方法的名稱更改,Visual Studio 的重構也會在此處重命名。

注意:“nameof”關鍵字在 5 年前發布問題時不可用。

測試班

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編寫重載。

讓我在這里對手頭的問題添加一些解釋。 我們正在尋找一個方法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方法,我仍然必須提供類型提示,因為該方法將來可能會被重載,然后代碼將不再編譯。

直接幫手

鑒於上述說明,我們可以提供以下一組輔助方法,以在一定程度上減輕轉換每個方法以獲取其信息的繁瑣任務:

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參數。 在那些特殊情況下,從 lambda 表達式中提取方法信息變得很方便,我們回到其他海報提供的解決方案(代碼靈感來自此處):

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

也許不是理想的方式,但它可以幫助

不使用 lambdas/表達式的解決方案:

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

我創建了一個 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 模板的Build Action設置為None
  • 您可以通過將 max 變量設置為適當的設置來創建更多或更少的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM