简体   繁体   English

从方法引用 C# 中获取方法信息

[英]get methodinfo from a method reference C#

We can use a C# typeof keyword when we want to get Type instance for specified type.当我们想要获取指定类型的 Type 实例时,我们可以使用 C# typeof关键字。 But what can I use if I want to get MethodInfo of a method by it's reference?但是如果我想通过它的引用获取方法的MethodInfo ,我可以使用什么?

For example I have a simple console app.例如,我有一个简单的控制台应用程序。 It contains Program.Main method.它包含Program.Main方法。 I want to get MethodInfo by using something like methodinfoof(Program.Main) .我想通过使用诸如methodinfoof(Program.Main)类的东西来获取MethodInfo I have this problem because the method names might change, so I cannot just use Type.GetMethodInfo(string MethodName) for that.我有这个问题,因为方法名称可能会改变,所以我不能只使用Type.GetMethodInfo(string MethodName)

I have about 10 000 methods for which I would like to get MethodInfo , so adding any custom attributes or anything else to my methods is not a solution.我有大约 10 000 个方法想要获取MethodInfo ,因此向我的方法添加任何自定义属性或其他任何内容都不是解决方案。

Slight adaptation of a previously posted answer, but this blog post seems to achieve what you're asking for;对先前发布的答案稍作改编,但这篇博文似乎实现了您的要求; http://blog.functionalfun.net/2009/10/getting-methodinfo-of-generic-method.html http://blog.functionalfun.net/2009/10/getting-methodinfo-of-generic-method.html

Sample usage would be as follows;示例用法如下;

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

Original answer was to this question;最初的答案是这个问题; https://stackoverflow.com/a/9132588/5827 https://stackoverflow.com/a/9132588/5827

You can use expression trees for non-static methods.您可以将表达式树用于非静态方法。 Here is an example.这是一个例子。

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");
    }
}

You would use it like this:你会像这样使用它:

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

Test() is a member function declared in the Program class. Test() 是在 Program 类中声明的成员函数。

Use MemberExpression and MemberInfo instead if you want to support property getters and setters.如果您想支持属性 getter 和 setter,请改用MemberExpressionMemberInfo

I know this is a very old post, but I'll just throw this out there for someone who might still be looking for a simple solution to this.. It just seems like no one thought of the simplest solution:我知道这是一篇很老的帖子,但我会把它扔给那些可能仍在寻找简单解决方案的人。 似乎没有人想到最简单的解决方案:

typeof(Program).GetMethods();

Returns an array with the MethodInfo of all methods in the Program class, regardless of attributes or of having parameters or not.返回一个包含 Program 类中所有方法的 MethodInfo 的数组,而不管属性或是否有参数。

You can iterate ove it if you want, for instance, to list the names of all your 10.000+ methods.例如,如果您想列出所有 10.000 多个方法的名称,您可以对其进行迭代。

You can also do typeof(Program).GetMethod(nameof(Program.Main));你也可以做typeof(Program).GetMethod(nameof(Program.Main)); this way if the method's name change Visual Studio's refactoring will rename it here too.这样,如果方法的名称更改,Visual Studio 的重构也会在此处重命名。

NOTE: The "nameof" keyword was not available 5 years ago when question was posted.注意:“nameof”关键字在 5 年前发布问题时不可用。

Test class测试班

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

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

And you can do something like this你可以做这样的事情

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

Update更新
Per @Greg comment if you have some parameters to the methods, you can use Action<T> , Action<T1, T2> , Action<T1, T2, T3> , or Func<T1> , the inconvenience is that you will still need to write the overloads for GetMethodInfo .根据@Greg 评论,如果您有一些方法参数,您可以使用Action<T>Action<T1, T2>Action<T1, T2, T3>Func<T1> ,不便之处在于您仍然会需要为GetMethodInfo编写重载。

Let me add some explanations to the problem at hands here.让我在这里对手头的问题添加一些解释。 We are looking for a method GetMethodInfo(SomeMethodSymbol) which returns information about the given method.我们正在寻找一个方法GetMethodInfo(SomeMethodSymbol) ,它返回有关给定方法的信息。 This is not straitforward because methods may be overloaded in C#.这并不直截了当,因为方法可能会在 C# 中重载。 So basically you need to provide additionnal cues into the call to make the compiler (and other code analysers like Intellisense) understand which method you are talking about.所以基本上你需要在调用中提供额外的提示,以使编译器(和其他代码分析器,如 Intellisense)理解你在谈论哪种方法。

Say for example I am looking for informations about the Math.Abs method.举例来说,我正在寻找有关Math.Abs方法的信息。 I must then specify which overloaded version of the method I am looking for exactly:然后我必须指定我正在寻找的方法的哪个重载版本:

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

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

Even if there is just one existing overload, like for the Math.Exp method, I must still provide typing cues, because the method might be overloaded in the future and the code would then not compile anymore.即使只有一个现有的重载,比如Math.Exp方法,我仍然必须提供类型提示,因为该方法将来可能会被重载,然后代码将不再编译。

Direct helpers直接帮手

Given the above remarks, we can provide the following set of helper methods to alleviate somewhat the tedious task of casting every method to reach its informations:鉴于上述说明,我们可以提供以下一组辅助方法,以在一定程度上减轻转换每个方法以获取其信息的繁琐任务:

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;
}

You would then use those helpers like this:然后你会像这样使用这些助手:

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),
};

Note that type information should still be provided except for void static method taking no arguments like Console.Clear .请注意,除了没有像Console.Clear这样的参数的 void 静态方法之外,仍然应该提供类型信息。 Also, for instance methods, an actual instance should be used to get the appropriate method, which uses more resources.此外,对于实例方法,应该使用实际实例来获取适当的方法,这会使用更多资源。

Indirect helpers间接帮手

Now for some corner cases the above helpers won't work.现在对于某些极端情况,上述助手将不起作用。 Say the method uses out parameters for example.例如,假设该方法使用out参数。 In those special cases, extracting method informations from lambda expressions becomes handy, and we get back to the solution provided by other posters (code inspiration from here ):在那些特殊情况下,从 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;
    }
}

You would use those like this:你会使用这样的:

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

Note that type information is still provided indirectly from argument type.请注意,类型信息仍然是从参数类型间接提供的。 Note as well that a dummy argument has been added just to make it possible to use an out parameter.还要注意,添加了一个虚拟参数只是为了可以使用out参数。

Complete demo program: https://dotnetfiddle.net/CkS075 .完整的演示程序: https : //dotnetfiddle.net/CkS075

Maybe not the ideal way, but it could help : 也许不是理想的方式,但它可以帮助

Solution without using lambdas/expressions:不使用 lambdas/表达式的解决方案:

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

I created a T4 template that creates the needed helper functions to help you with this.我创建了一个 T4 模板,它创建了所需的辅助函数来帮助您解决这个问题。 It creates a list of functions to get MethodInfo objects from Func<> or Action<> methods.它创建了一个函数列表,用于从 Func<> 或 Action<> 方法中获取 MethodInfo 对象。

Copy the following code into a file named GetMethodInfo.tt :将以下代码复制到名为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");
        }

<# } #>
    }
}

Notes :注意事项

  • please make sure, that the Build Action of the .tt template is set to None请确保 .tt 模板的Build Action设置为None
  • you can create more or less functions by setting the max variable to the appropriate setting.您可以通过将 max 变量设置为适当的设置来创建更多或更少的函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM