53

I have a weird situation where I need to get the Name of the delegate as a string. I have a generic method that looks like this.

private T Get<T>(T task, Action<T> method) where T : class
{
  string methodName = method.Method.Name //Should return Bark
}

and I am calling it like this

private void MakeDogBark()
{
  dog = Get(dog, x=>x.Bark());
}

But instead of seeing "Bark" I see this "<MakeDogBark>b__19". So it looks like it is giving me the method name that made the initial call instead of the name of the delegate.

Anyone know how to do this?

Adam
  • 1,561
  • 2
  • 15
  • 25

2 Answers2

78

It's giving you the name of the method which is the action of the delegate. That just happens to be implemented using a lambda expression.

You've currently got a delegate which in turn calls Bark. If you want to use Bark directly, you'll need to create an open delegate for the Bark method, which may not be terribly straightforward. That's assuming you actually want to call it. If you don't need to call it, or you know that it will be called on the first argument anyway, you could use:

private T Get<T>(T task, Action method) where T : class
{
   string methodName = method.Method.Name //Should return Bark
}

private void MakeDogBark()
{
   dog = Get(dog, dog.Bark);
}

You could get round this by making the parameter an expression tree instead of a delegate, but then it would only work if the lambda expression were just a method call anyway.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • That worked beautifully. I didn't need to execute it but I had to pass the method name to a class that would do something with it. Instead of just taking a string I wanted the type safety of the caller having to give me a real method. As always you are the man :) – Adam Sep 29 '09 at 23:50
  • @Jon Is there a way to do the same with a generic method? – Arnab Chakraborty Jun 09 '14 at 11:15
  • @ArnabChakraborty: You'd have to specify the type argument as well, but I believe it would then work. Can't easily try it right now though. – Jon Skeet Jun 09 '14 at 11:57
  • 1
    Where can I find more about the naming convention used to produce that "b__19" string? In my case, I am seeing "b__21_0". Thank you. – Sabuncu May 10 '18 at 10:48
  • @Sabuncu: It's not a matter of a convention, it's just whatever the compiler decides to use. There are no guarantees around it, other than that the name wouldn't be valid in regular C#. – Jon Skeet May 10 '18 at 14:39
  • Is there a similar answer for a Func<>? I see a lot of Action in the answers I am Googling for but not much about how to do similar for Func<> – Thomas927 Apr 29 '23 at 04:10
  • @Thomas927: What have you tried so far? (It should just be *exactly* the same...) – Jon Skeet Apr 29 '23 at 06:22
6

You can get the name of the method call by making the parameter an expression instead of a delegate, just like Jon mentioned

private T Get<T>(T task, Expression<Action<T>> method) where T : class
{
    if (method.Body.NodeType == ExpressionType.Call)
    {
        var info = (MethodCallExpression)method.Body;
        var name = info.Method.Name; // Will return "Bark"
    }

    //.....
}
Rohan West
  • 9,262
  • 3
  • 37
  • 64
  • 2
    So you're saying it would be called like `Get(x => x.DoSomething(Arg1))`? What if you just wanted to pass the method as a delegate in a type safe way but without invoking it, like this `Get(x => x.DoSomething)`? – bflemi3 May 21 '13 at 10:47
  • Yes. Essentially, is there a way to do the `nameof` operator from C# 6 in C# 5? – Katie Kilian Feb 12 '15 at 22:24