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

发布于 2024-12-17 09:59:38 字数 829 浏览 3 评论 0原文

我知道网站上有一些关于此问题的答案,如果这有任何重复,我深表歉意,但我发现的所有答案都没有做我想做的事情。

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

假设我想获取此接口中方法的名称:

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

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

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

但所有尝试都无法编译

有没有办法做到这一点?

I know there are a few answers on the site on this and i apologize if this is in any way duplicate, but all of the ones I found does not do what I am trying to do.

I am trying to specify method info so I can get the name in a type safe way by not using strings.
So I am trying to extract it with an expression.

Say I want to get the name of a method in this interface:

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

Currently I can get the name using THIS method:

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

I can call the helper method as follows:

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

But I am looking for the version that I can get the method name without specifying the parameters (null, null)

like this:

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

But all attempts fail to compile

Is there a way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

神经暖 2024-12-24 09:59:38
x => x.DoSomething

为了使其可编译,我只看到两种方法:

  1. 采用非通用方式并将其参数指定为 Action
  2. Action 指定为您自己的目标委托类型:GetMethodInfo(x => new Action(x.DoSomething))

如果您可以使用第二个,它允许您省略参数,然后您可以编写您的 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;
    }

它适用于您的接口,但可能需要一些概括才能使其适用于任何接口方法,就看你了。

x => x.DoSomething

In order to make this compilable I see only two ways:

  1. Go non-generic way and specify it's parameter as Action<string, string>
  2. Specify Action<string, string> as your target delegate type by yourself: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))

if you are ok to go with second one, which allows you to omit arguments then you can write your GetMethodInfo method as follows:

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

It works for your interface, but probably some generalization will be required to make this working with any method, that's up to you.

梦言归人 2024-12-24 09:59:38

以下内容与 .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;
    }
}

查看Ideone 上的此示例代码
请注意,Ideone 没有 .NET 4.5。

The following is compatible with .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;
}

You can use it with expressions like x => x.DoSomething, however it would require some wrapping into generic methods for different types of methods.

Here is a backwards-compatible version:

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

Check this sample code on Ideone.
Note, that Ideone does not have .NET 4.5.

末骤雨初歇 2024-12-24 09:59:38

问题是 x.DoSomething 代表一个方法组。并且您必须以某种方式明确指定您想要将该方法组转换为哪种委托类型,以便可以选择该组的正确成员。该组是否只包含一名成员也没关系。

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

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

The problem with this is that x.DoSomething represents a method group. And you have to somehow explicitly specify what delegate type do you want to convert that method group into, so that the correct member of the group can be selected. And it doesn't matter if that group contains only one member.

The compiler could infer that you mean that one, but it doesn't do that. (I think it's this way so that your code won't break if you add another overload of that method.)

Snowbear's answer contains good advice on possible solutions.

∞琼窗梦回ˉ 2024-12-24 09:59:38

这是对老问题的新答案,但回应了对已接受答案的“冗长”抱怨。它需要更多代码,但结果是这样的语法:

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<> 一样和 Func<>委托最多 16 个参数,您必须拥有最多接受 16 个参数的 GetActionInfo 和 GetFuncInfo 方法(或更多,尽管我认为如果您有具有 16 个参数的方法,重构是明智的)。更多的代码,但语法有所改进。

This is a new answer to an old question, but responds to the "verbose" complaint of the accepted answer. It requires more code, but the result is a syntax like:

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

or, for methods with a return value

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

where

object DoSomethingWithReturn(string param1, string param2);

Just like the framework provides Action<> and Func<> delegates up to 16 parameters, you have to have GetActionInfo and GetFuncInfo methods that accept up to 16 parameters (or more, although I'd think refactoring is wise if you have methods with 16 parameters). A lot more code, but an improvement in the syntax.

╰つ倒转 2024-12-24 09:59:38

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

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

// 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));

If you are ok with using the nameof() operator you can use the following approach.

One of the benefits is not having to unwrap an expression tree or supply default values or worry about having a non-null instance of the type with the method.

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

Usage:

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));
我一向站在原地 2024-12-24 09:59:38

如果您的应用程序允许依赖 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);
}

If your application would allow a dependency on Moq (or a similar library), you could do something like this:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文