7

I am using a generic handler in Visual Studio 2013.

What I am trying to do is to create a URL that incorporates the name of a method, but I want the name of the method to be real code so that it will not be hard-coded and follow along if the function name was changed.

If I were doing this in C or C++, I would have just said:

#define GENERATE_REFERENCE(text) #text

I don't really care it is formed as a method call as I have prototyped here

"Pseudo-code" in C# of what I am trying to do:

public class MyClass {
    public void SayHello (String name)
    {
    ...
    }

    public void GenerateLink()
    {
         url = "... "+GenerateReference(this.SayHello);
         // url would be "... SayHello";

    }
    public String GenerateReference( DataType method )
    {
         // Is there some way to do this?
         return method.MethodName.ToString();
    }
}

My question is different than the suggested duplicate question get methodinfo from a method reference C# because my question comes from a place of great ignorance of C# mechanisms (neophyte). The suspected duplicate question implies a much higher level of understanding well beyond what I demonstrated in my question - I didn't know enough to ask that question. I would have never have found this answer from my searching.

Community
  • 1
  • 1
craigdfrench
  • 972
  • 10
  • 20
  • 1
    Possible duplicate of [get methodinfo from a method reference C#](http://stackoverflow.com/questions/9382216/get-methodinfo-from-a-method-reference-c-sharp) – juharr Nov 10 '15 at 17:27

6 Answers6

25

C# 6

nameof(MyClass.SayHello)

Before C# 6

public static String GenerateReference<T>(Expression<Action<T>> expression)
{
    var member = expression.Body as MethodCallExpression;

    if (member != null)
        return member.Method.Name;

    throw new ArgumentException("Expression is not a method", "expression");
}

GenerateReference<MyClass>(c => c.SayHello(null));

Credit goes to https://stackoverflow.com/a/9382501/471321

Community
  • 1
  • 1
Steven Wexler
  • 16,589
  • 8
  • 53
  • 80
11

c# 6 introduces a new operator called nameof which eliminates these hardcoded string with method names.

You can use it as follows: nameof(Class.Method)

Arthur Rizzo
  • 1,337
  • 15
  • 30
3

C#6.0 Does include the nameof feature. However, knowing you're in VS2013 and C#6.0 is not enabled by default; I suggest using System.Reflection.

I would use something as such.

System.Reflection.MethodBase.GetCurrentMethod().Name

and pass the name of the method as a parameter if needed.

Cheers!

Brady Liles
  • 713
  • 7
  • 13
3

This is probably the simplest way for your use case;

Action<string> del = this.SayHello;
string ret = del.Method.Name;
Brian Rudolph
  • 6,142
  • 2
  • 23
  • 19
  • That's a good suggestion. It has a couple of drawbacks (all solutions other than `nameof` do) -- it will cause compiler errors if you change the signature of `SayHello`, and it also has runtime overhead. Those might be acceptable drawbacks. – Peter Torr - MSFT Nov 11 '15 at 17:55
  • Considering the OP use case of generating a url for a service call, I would say that the compiler error is actually a benefit, it makes this very explicit. As for the runtime overhead, I agree, however other than a compile-time feature such as nameof or [CallerMemberName], I think it is about on-par with the other options. – Brian Rudolph Nov 11 '15 at 18:18
  • A compiler error won't fix the URL string though if it needs different arguments. Really I think the right way to do this is have a Single Source of Truth and then generate the client and server code from it... but that's a bit beyond the original question. – Peter Torr - MSFT Nov 11 '15 at 20:09
2

You can use the CallerMemberNameAttribute on the parameter in order to have the compiler insert the name for you in earlier versions of C#.

Here is a sample that relies on overloading to get the right answer. Note that if your "real" methods all have unique parameters of their own, you don't need the dummy overload and can avoid the QueryMethodNameHelper argument altogether

// This class is used both as a dummy parameter for overload resolution
// and to hold the GetMyName method. You can call it whatever you want
class QueryMethodNameHelper
{
  private QueryMethodNameHelper() { }

  public static readonly QueryMethodNameHelper Instance = 
    new QueryMethodNameHelper();

  public static string GetMyName([CallerMemberName] string 
    name = "[unknown]")
  {
    return name;
  }
}

class Program
{
  // The real method
  static void SayHello()
  {
    Console.WriteLine("Hello!");
  }

  // The dummy method; the parameter is never used, but it ensures
  // we can have an overload that returns the string name
  static string SayHello(QueryMethodNameHelper dummy)
  {
    return QueryMethodNameHelper.GetMyName();
  }

  // Second real method that has an argument
  static void DoStuff(int value)
  {
    Console.WriteLine("Doing stuff... " + value);
  }

  // Dummy method can use default parameter because
  // there is no ambiguity
  static string DoStuff(QueryMethodNameHelper dummy = null)
  {
    return QueryMethodNameHelper.GetMyName();
  }

  static void Main(string[] args)
  {
    string s = SayHello(QueryMethodNameHelper.Instance);
    Console.WriteLine(s);
    SayHello();

    string s2 = DoStuff();
    Console.WriteLine(s2);
    DoStuff(42);
  }
}

This sample has the benefit of injecting the string at compile-time (there is no runtime overhead to lookup metadata) but it does require you to keep the method names in sync (eg, if you rename the "real" SayHello you also need to rename the helper SayHello). Luckily the Refactor dialog will do that for you if you click the "Rename overloads" checkbox, but it's not on by default.

Peter Torr - MSFT
  • 11,824
  • 3
  • 18
  • 51
  • This only works if you are calling GenerateReference from SayHello, which is not the case... – Brian Rudolph Nov 10 '15 at 17:31
  • I think that if I put the CallerMemberNameAttribute call in GenerateReference it would work as the question is posed. – craigdfrench Nov 10 '15 at 17:34
  • @silverglade only if you are calling GenerateReference from SayHello, which in your example, you are not. – Brian Rudolph Nov 10 '15 at 17:36
  • @Brian Rudolph, yes now I understand. Thanks for clarifying that! – craigdfrench Nov 10 '15 at 17:52
  • You can use an overloaded form of SayHello that takes a dummy parameter and returns a string built by the helper. I'll try to add sample code later. – Peter Torr - MSFT Nov 10 '15 at 20:42
  • My problem with this design is that it fundamentally leaves us with the same issue as hard coding a string, since there is no compile-time link between the dummy method and the actual method. One could get refactored without the other. This is a lot of work to generate the equivalent of a constant string. – Brian Rudolph Nov 11 '15 at 18:26
  • Agreed; it all depends on what you're optimizing for. There is no perfect solution pre-`nameof`. And even with `nameof` or the delegate trick, it doesn't help if you change the design so that `SayHello` is no longer the correct method -- as long as it exists (with the right signature) the compiler won't complain. – Peter Torr - MSFT Nov 11 '15 at 20:00
  • I ended up using the [CallerMemberName] concept in my solution, so I will accept this as my accepted answer. Great discussion folks! – craigdfrench Dec 14 '15 at 17:12
0

Use System.Reflection (pre C#6.0):

typeof(MyClass).GetMethod("SayHello").Name

or nameof for C#6.0 and later:

nameof(SayHello)

Marco Fatica
  • 937
  • 7
  • 12
  • 3
    But this isn't really any difference to using "SayHello". You need the name of the method to get the name of the method – Liam Nov 10 '15 at 17:22