使用表达式获取方法的名称

时间:2011-11-22 10:28:37

标签: c# expression lambda

我知道网站上有一些答案,如果这有任何重复,我道歉,但我找到的所有答案都没有做我想做的事。

我正在尝试指定方法信息,因此我可以通过不使用字符串以类型安全的方式获取名称。 所以我试图用表达式提取它。

假设我想在此界面中获取方法的名称:

public interface IMyInteface
{
    void DoSomething(string param1, string param2);
}

目前我可以使用THIS方法获取名称:

 MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression)
 {
        return ((MethodCallExpression)expression.Body).Method;
 }

我可以按如下方式调用辅助方法:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null));
Console.WriteLine(methodInfo.Name);

但是我正在寻找可以在不指定参数的情况下获取方法名称的版本(null,null)

像这样:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething);

但所有尝试都无法编译

有办法做到这一点吗?

6 个答案:

答案 0 :(得分:14)

x => x.DoSomething

为了使这个可编辑,我只看到两种方式:

  1. 采用非通用方式,并将其参数指定为Action<string, string>
  2. 自行指定Action<string, string>作为目标代理类型:GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))
  3. 如果您可以选择第二个,这可以省略参数,那么您可以按如下方式编写GetMethodInfo方法:

        MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression)
        {
            var unaryExpression = (UnaryExpression) expression.Body;
            var methodCallExpression = (MethodCallExpression) unaryExpression.Operand;
            var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last();
            var methodInfo = (MemberInfo) methodInfoExpression.Value;
            return methodInfo;
        }
    

    它适用于您的界面,但可能需要进行一些概括才能使其适用于任何方法,这取决于您。

答案 1 :(得分:12)

以下与.NET 4.5兼容:

public static string MethodName(LambdaExpression expression)
{
    var unaryExpression = (UnaryExpression)expression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    var methodCallObject = (ConstantExpression)methodCallExpression.Object;
    var methodInfo = (MethodInfo)methodCallObject.Value;

    return methodInfo.Name;
}

您可以将它与x => x.DoSomething这样的表达式一起使用,但是它需要为不同类型的方法包含一些通用方法。

这是一个向后兼容的版本:

private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null;

public static string MethodName(LambdaExpression expression)
{
    var unaryExpression = (UnaryExpression)expression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    if (IsNET45)
    {
        var methodCallObject = (ConstantExpression)methodCallExpression.Object;
        var methodInfo = (MethodInfo)methodCallObject.Value;
        return methodInfo.Name;
    }
    else
    {
        var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
        var methodInfo = (MemberInfo)methodInfoExpression.Value;
        return methodInfo.Name;
    }
}

检查this sample code on Ideone。 请注意,Ideone没有.NET 4.5。

答案 2 :(得分:4)

问题在于x.DoSomething表示方法组。并且您必须以某种方式明确指定要将该方法组转换为哪种委托类型,以便可以选择该组的正确成员。如果该组只包含一个成员,则无关紧要。

编译器可以推断你的意思是那个,但它没有那样做。 (我认为就是这样,如果你添加该方法的另一个重载,你的代码就不会破坏。)

Snowbear的回答包含对可能解决方案的良好建议。

答案 3 :(得分:2)

这是一个旧问题的新答案,但回应了接受答案的“冗长”投诉。它需要更多代码,但结果是语法如下:

MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething);

或者,对于具有返回值的方法

MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn);  

其中

object DoSomethingWithReturn(string param1, string param2);

就像框架提供Action&lt;&gt;和Func&lt;&gt;委托最多16个参数,你必须有GetActionInfo和GetFuncInfo方法接受多达16个参数(或更多,但我认为如果你有16个参数的方法,重构是明智的)。代码更多,但语法有所改进。

答案 4 :(得分:1)

如果您的应用程序允许依赖Moq(或类似的库),您可以执行以下操作:

class Program
{
    static void Main(string[] args)
    {
        var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething));
        Console.WriteLine(methodName);
    }

    static string GetMethodName<T>(Func<T, Delegate> func) where T : class
    {
        // http://code.google.com/p/moq/
        var moq = new Mock<T>();
        var del = func.Invoke(moq.Object);
        return del.Method.Name;
    }
}

public interface IMyInteface
{
    void DoSomething(string param1, string param2);
}

答案 5 :(得分:0)

如果可以使用nameof()运算符,则可以使用以下方法。

好处之一是不必解开表达式树或提供默认值,也不必担心该方法具有该类型的非null实例。

// As extension method
public static string GetMethodName<T>(this T instance, Func<T, string> nameofMethod) where T : class
{
    return nameofMethod(instance);
}

// As static method
public static string GetMethodName<T>(Func<T, string> nameofMethod) where T : class
{
    return nameofMethod(default);
}

用法:

public class Car
{
    public void Drive() { }
}

var car = new Car();

string methodName1 = car.GetMethodName(c => nameof(c.Drive));

var nullCar = new Car();

string methodName2 = nullCar.GetMethodName(c => nameof(c.Drive));

string methodName3 = GetMethodName<Car>(c => nameof(c.Drive));