13

I'm trying to get the method name of a function passed into an object using a .Net closure like this:

Method Signature

public IEnumerable<T> GetData<T>(Func<IEnumerable<T>> WebServiceCallback) 
where T : class    
{
    // either gets me '<LoadData>b__3'
    var a = nrdsWebServiceCallback.Method.Name;
    var b = nrdsWebServiceCallback.GetInvocationList();

    return WebServiceCallback();
}

I'm calling it like this:

SessionStateService.Labs = CacheManager.GetData(() =>  
WCFService.GetLabs(SessionStateService.var1, SessionStateService.var2));

Seeing 'b__3' instead of WCFServce.GetLabs(..) etc

Barry McDermid
  • 319
  • 3
  • 10
  • Reading up on this would help you: http://msdn.microsoft.com/en-us/library/bb397687.aspx It is possible to get the name from parsing the expression tree though, but why do you need this? – flindeberg Dec 06 '13 at 09:36
  • You get `b__3` because that's the name of the compiler-generated function. Your delegate knows nothing of `WCFService.GetLabs`, that's contained entirely within `b__3`. –  Dec 06 '13 at 09:38
  • Why do you need this? For logging. – StingyJack Jul 17 '20 at 19:42

2 Answers2

20

You're looking at the name of the lambda expression (generated by the compiler), instead of the name of the method called inside the lambda.

You have to use an <Expression<Func<T>> instead of a Func<T>. Expressions can be parsed and analyzed.

Try

public IEnumerable<T> GetData<T>(Expression<Func<IEnumerable<T>>> callbackExpression) 
where T : class    
{
    var methodCall = callbackExpression.Body as MethodCallExpression;
    if(methodCall != null)
    {
        string methodName = methodCall.Method.Name;
    }

    return callbackExpression.Compile()();
}
dcastro
  • 66,540
  • 21
  • 145
  • 155
  • `return WebServiceCallback();` won't compile like this, because you removed `WebServiceCallback`. –  Dec 06 '13 at 09:38
  • @hvd Thanks, i didn't notice that. Just fixed it. – dcastro Dec 06 '13 at 09:40
  • Looks good to me now as an answer to the question as asked. I have my doubts about the underlying problem that makes the OP want to analyse the function, though, but given the limited information included in the question there's not much that can be said about that. –  Dec 06 '13 at 09:49
  • @hvd Me too - expression analysis is only required to solve a few very specific problems. Maybe he's just exploring the language? – dcastro Dec 06 '13 at 09:52
  • I'm wrapping a series of existing WS calls in a Cache Manager. It must decide whether to use the existing WS call (slower) or use the local DB cache (speed improvement). I need a way of linking the specific DB Cache table with each method (each has a different cache table). My first though was I could use the method name to do this. I could of course pass it in as a string parm and do the same. – Barry McDermid Dec 06 '13 at 10:16
  • 1
    @BarryMcDermid Depending on how your service is structured, making it depend on `typeof(T)` could be enough. If multiple services do return the same type, my personal preference would be to write it as `SessionStateService.Labs = CacheManager.GetData(WCFService.GetLabs, SessionStateService.var1, SessionStateService.var2);` where the first parameter is a delegate (not an expression tree). –  Dec 06 '13 at 10:28
  • @hvd Good suggestion. Considered that. The services all use IEnumerable<..> for return types but the specific type they return varies thus using IEnumerable for the return type. – Barry McDermid Dec 06 '13 at 10:47
  • Does this approach have some performance issue or something to take care? – Andre Apr 02 '19 at 13:45
2

What is actually passed into your function is an anonymous lambda function (() => WCFService.Etc), so what you're seeing is the actual method name - <LoadData>b__3 is the autogenerated name for the anonymous method.

What you actually want is the method called inside the method called. For that, you need to delve into expressions. Instead of Func<IEnumerable<T>>, define your parameter as Expression<Func<IEnumerable<T>>>, and call this code:

var body = nrdsWebServiceCallback.Body as MethodCallExpression;
if (body != null)
   Console.WriteLine(body.Method.DeclaringType.Name + "." + body.Method.Name);
Kate Gregory
  • 18,808
  • 8
  • 56
  • 85
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63