Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting a class method name using Lambda Expression

Currently in my code I have a method like this:

.AddMethod(nameof(IRoleService.GetRoles))

What Im trying to do is selecting the interface method using lambda expression and then get the name of method inside AddMethod. So the result would be :

.AddMethod<IRoleService>(x=> x.GetRoles)

I have tried this:

AddMethod<T>(Expression<Func<T, Action>> expression);

But the problem is some of the interface methods have input parameters which I'm trying to ignore, as I just need the method name. Would be thankful if you help me with this.

Alex

like image 956
Alex Avatar asked Oct 19 '22 12:10

Alex


2 Answers

But the problem is some of the interface methods have input parameters which I'm trying to ignore

You can't ignore input parameters unless these parameters have default values.

If you can't change your code to set default values to these methods' input parameters, you'll need to provide default values yourself using reflection to get the so-called input parameters and give them some values. Or you can just give them during compile-time:

// I don't know which parameters accept your methods...
AddMethod<IRoleService>(x => x.GetRoles("arg1", 2));

OP said...

considering I just need the Method Name, Do you suggest a clean way to get the name without passing parameters?

There's no dirty or clean way of doing so. Expression trees are still valid code (i.e. it should be code that can be built into executable code).

How you would produce an expression tree where you access a method name instead of calling it? Or how you access a method to call it without parameters if the whole method has parameters? Would be this valid C# code? The answer is no.

like image 76
Matías Fidemraizer Avatar answered Oct 27 '22 00:10

Matías Fidemraizer


This can be achieved with a little bit of digging into the LambdaExpression like this:

string GetMethodCallName(LambdaExpression expression)
{
    var unary = (UnaryExpression)expression.Body;
    var methodCall = (MethodCallExpression)unary.Operand;
    var constant = (ConstantExpression)methodCall.Object;
    var memberInfo = (MemberInfo)constant.Value;

    return memberInfo.Name;
}

Unfortunately GetMethodCallName cannot be called directly with the x => x.GetRoles syntax you want since there is no type argument to define what x is. This means you'll need to create an overload of AddMethod for every number of input arguments you want to support for both Action and Func:

void AddMethod<T>(Expression<Func<T, Action>> expression);
void AddMethod<T, Arg1>(Expression<Func<T, Action<Arg1>>> expression);
void AddMethod<T, Arg1, Arg2>(Expression<Func<T, Action<Arg1, Arg2>>> expression);

void AddMethod<T, TResult>(Expression<Func<T, Func<TResult>>> expression);
void AddMethod<T, Arg1, TResult>(Expression<Func<T, Func<Arg1, TResult>>> expression);
void AddMethod<T, Arg1, Arg2, TResult>(Expression<Func<T, Func<Arg1, Arg2, TResult>>> expression);

Then in each AddMethod implementation call GetMethodCallName with the expression argument:

void AddMethod<T, Arg1>(Expression<Func<T, Action<Arg1>>> expression)
{
    var methodName = GetMethodCallName(expression);
}

Then you can call AddMethod and specify the containing type and the return and input types of the method:

.AddMethod<IRoleService>(x => x.GetRoles);
.AddMethod<IRoleService, TResult>(x => x.GetRoles);
.AddMethod<IRoleService, Arg1, TResult>(x => x.GetRoles);

Where:

  • TResult is the return type of .GetRoles
  • Arg1 is the type of the first argument of .GetRoles
like image 36
John Avatar answered Oct 27 '22 00:10

John