Get the name of a method using an expression

后端 未结 6 1147
被撕碎了的回忆
被撕碎了的回忆 2020-11-28 12:03

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

相关标签:
6条回答
  • 2020-11-28 12:16

    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.

    0 讨论(0)
  • 2020-11-28 12:17

    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.

    0 讨论(0)
  • 2020-11-28 12:19
    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.

    0 讨论(0)
  • 2020-11-28 12:29

    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));
    
    0 讨论(0)
  • 2020-11-28 12:34

    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.

    0 讨论(0)
  • 2020-11-28 12:41

    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);
    }
    
    0 讨论(0)
提交回复
热议问题