ChatGPT解决这个技术问题 Extra ChatGPT

Can you use reflection to find the name of the currently executing method?

Like the title says: Can reflection give you the name of the currently executing method.

I'm inclined to guess not, because of the Heisenberg problem. How do you call a method that will tell you the current method without changing what the current method is? But I'm hoping someone can prove me wrong there.

Update:

Part 2: Could this be used to look inside code for a property as well?

Part 3: What would the performance be like?

Final Result I learned about MethodBase.GetCurrentMethod(). I also learned that not only can I create a stack trace, I can create only the exact frame I need if I want.

To use this inside a property, just take a .Substring(4) to remove the 'set_' or 'get_'.

Joel, I know its an old question, but what do you mean by creating exact frame of a method?
It refers to a specific item in the call stack: the portion of the stack trace that matters.
For async methods you can use GetMethodContextName() from Using System.Reflection to Get a Method's Full Name

V
Victor Yarema

For non-async methods one can use

System.Reflection.MethodBase.GetCurrentMethod().Name;

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod

Please remember that for async methods it will return "MoveNext".


Be aware that this doesn't always yield the expected results. I.e., small methods or properties are often inlined in release builds, in which case the result will be the caller's method name instead.
As far as I know, no. Because in runtime, the MSIL is not available anymore from the execution pointer (it's JITted). You can still use reflection if you know the name of the method. The point is, when inlined, the currently executing method is now another method (i.e., one or more higher up the stack). In other words, the method disappeared. Even if you mark your method with NoInlining, there's still a chance it gets tail-call optimized, in which case it's gone too. It will work, however, while in debug build.
In order to avoid inline add [MethodImpl(MethodImplOptions.NoInlining)] attribute on top of the method.
Inside async method you will most likely get "MoveNext" as method name.
Is there a way to get the current method name of an async method?
P
Pang

As of .NET 4.5, you can also use [CallerMemberName].

Example: a property setter (to answer part 2):

protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
{
    this.propertyValues[property] = value;
    OnPropertyChanged(property);
}

public string SomeProperty
{
    set { SetProperty(value); }
}

The compiler will supply matching string literals at call sites, so there is basically no performance overhead.


This is great! I was using the StackFrame(1) method described in other answers for logging, which seemed to work until the Jitter decided to start inlining things. I did not want to add the attribute to prevent inlining for performance reasons. Using the [CallerMemberName] approach fixed the issue. Thanks!
[CallerMemberName] is availably at net 4 with the BCL build packed
Take into account that using StackFrame(1) in debug mode should work. But when using release mode when compiling, there might be some optimizations and the stack may not be what you expect.
Won't this return the calling member (i.e. SomeProperty), instead of the currently executing method?
Yes, invoking the setter will result in a call to OnPropertyChanged("SomeProperty") and not OnPropertyChanged("SetProperty")
J
Joel Coehoorn

The snippet provided by Lex was a little long, so I'm pointing out the important part since no one else used the exact same technique:

string MethodName = new StackFrame(0).GetMethod().Name;

This should return identical results to the MethodBase.GetCurrentMethod().Name technique, but it's still worth pointing out because I could implement this once in its own method using index 1 for the previous method and call it from a number of different properties. Also, it only returns one frame rather then the entire stack trace:

private string GetPropertyName()
{  //.SubString(4) strips the property prefix (get|set) from the name
    return new StackFrame(1).GetMethod().Name.Substring(4);
}

It's a one-liner, too ;)


can be public static string GetPropertyName() in a helper class ? static method ?
Same as with Ed Guiness's answer: the stack can be different in release builds and the first method may not be the same as the current method in cases of inlining or tail call optimization.
See John Nilsson's answer for a nice way around the inlining issue if you are using .Net 4.5.
this could be better than accepted answer and above answer too
L
Lars Mæhlum

Try this inside the Main method in an empty console program:

MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);

Console Output:
Main


C
Community

Comparing ways to get the method name -- using an arbitrary timing construct in LinqPad:

CODE

void Main()
{
    // from http://blogs.msdn.com/b/webdevelopertips/archive/2009/06/23/tip-83-did-you-know-you-can-get-the-name-of-the-calling-method-from-the-stack-using-reflection.aspx
    // and https://stackoverflow.com/questions/2652460/c-sharp-how-to-get-the-name-of-the-current-method-from-code

    var fn = new methods();

    fn.reflection().Dump("reflection");
    fn.stacktrace().Dump("stacktrace");
    fn.inlineconstant().Dump("inlineconstant");
    fn.constant().Dump("constant");
    fn.expr().Dump("expr");
    fn.exprmember().Dump("exprmember");
    fn.callermember().Dump("callermember");

    new Perf {
        { "reflection", n => fn.reflection() },
        { "stacktrace", n => fn.stacktrace() },
        { "inlineconstant", n => fn.inlineconstant() },
        { "constant", n => fn.constant() },
        { "expr", n => fn.expr() },
        { "exprmember", n => fn.exprmember() },
        { "callermember", n => fn.callermember() },
    }.Vs("Method name retrieval");
}

// Define other methods and classes here
class methods {
    public string reflection() {
        return System.Reflection.MethodBase.GetCurrentMethod().Name;
    }
    public string stacktrace() {
        return new StackTrace().GetFrame(0).GetMethod().Name;
    }
    public string inlineconstant() {
        return "inlineconstant";
    }
    const string CONSTANT_NAME = "constant";
    public string constant() {
        return CONSTANT_NAME;
    }
    public string expr() {
        Expression<Func<methods, string>> ex = e => e.expr();
        return ex.ToString();
    }
    public string exprmember() {
        return expressionName<methods,string>(e => e.exprmember);
    }
    protected string expressionName<T,P>(Expression<Func<T,Func<P>>> action) {
        // https://stackoverflow.com/a/9015598/1037948
        return ((((action.Body as UnaryExpression).Operand as MethodCallExpression).Object as ConstantExpression).Value as MethodInfo).Name;
    }
    public string callermember([CallerMemberName]string name = null) {
        return name;
    }
}

RESULTS

reflection reflection

stacktrace stacktrace

inlineconstant inlineconstant

constant constant

expr e => e.expr()

exprmember exprmember

callermember Main

Method name retrieval: (reflection) vs (stacktrace) vs (inlineconstant) vs (constant) vs (expr) vs (exprmember) vs (callermember) 

 154673 ticks elapsed ( 15.4673 ms) - reflection
2588601 ticks elapsed (258.8601 ms) - stacktrace
   1985 ticks elapsed (  0.1985 ms) - inlineconstant
   1385 ticks elapsed (  0.1385 ms) - constant
1366706 ticks elapsed (136.6706 ms) - expr
 775160 ticks elapsed ( 77.516  ms) - exprmember
   2073 ticks elapsed (  0.2073 ms) - callermember


>> winner: constant

Note that the expr and callermember methods aren't quite "right". And there you see a repetition of a related comment that reflection is ~15x faster than stacktrace.


L
Lex

Yes definitely.

If you want an object to manipulate I actually use a function like this:

public static T CreateWrapper<T>(Exception innerException, params object[] parameterValues) where T : Exception, new()
{
    if (parameterValues == null)
    {
        parameterValues = new object[0];
    }

    Exception exception   = null;
    StringBuilder builder = new StringBuilder();
    MethodBase method     = new StackFrame(2).GetMethod();
    ParameterInfo[] parameters = method.GetParameters();
    builder.AppendFormat(CultureInfo.InvariantCulture, ExceptionFormat, new object[] { method.DeclaringType.Name, method.Name });
    if ((parameters.Length > 0) || (parameterValues.Length > 0))
    {
        builder.Append(GetParameterList(parameters, parameterValues));
    }

    exception = (Exception)Activator.CreateInstance(typeof(T), new object[] { builder.ToString(), innerException });
    return (T)exception;
}

This line:

MethodBase method     = new StackFrame(2).GetMethod();

Walks up the stack frame to find the calling method then we use reflection to obtain parameter information values passed to it for a generic error reporting function. To get the current method simply use current stack frame (1) instead.

As others have said for the current methods name you can also use:

MethodBase.GetCurrentMethod()

I prefer walking the stack because if look internally at that method it simply creates a StackCrawlMark anyway. Addressing the Stack directly seems clearer to me

Post 4.5 you can now use the [CallerMemberNameAttribute] as part of the method parameters to get a string of the method name - this may help in some scenarios (but really in say the example above)

public void Foo ([CallerMemberName] string methodName = null)

This seemed to be mainly a solution for INotifyPropertyChanged support where previously you had strings littered all through your event code.


Not silly; I simply passed them in. You could probably do something to make it more simple to look at but the effort to reward ratio seemed to favour keeping it simple. Essentially the dev just copies in the parameter list of the method signature (removing types of course).
what it is: ExceptionFormat and GetParameterList?
Way late in the reply but: ExceptionFormat is a constant string format and GetParameterList is a simply function that formats the parameters with the values (you could do this inline)
d
denis phillips

EDIT: MethodBase is probably a better way to just get the method you're in (as opposed to the whole calling stack). I'd still be concerned about inlining however.

You can use a StackTrace within the method:

StackTrace st = new StackTrace(true);

And the look at the frames:

// The first frame will be the method you want (However, see caution below)
st.GetFrames();

However, be aware that if the method is inlined, you will not be inside the method you think you are. You can use an attribute to prevent inlining:

[MethodImpl(MethodImplOptions.NoInlining)]

Inline due to Release optimization is especially tricky since the code will behave differently in Debug and Release configurations. Watch out for small properties, they are the most likely victims of this.
I wonder why woud you use new StackTrace(true) instead of new StackTrace(false). Setting that to true will cause the stack trace to atttempt capturing the file name, line number and etc, which might make this call slower. Otherwise, a nice answer
S
Sunny Patel

The simple way to deal is:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;

If the System.Reflection is included in the using block:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;

U
Useme Alehosaini

For Async Methods, you can use:

//using System.Reflection;

var myMethodName = MethodBase
                    .GetCurrentMethod()
                    .DeclaringType
                    .Name
                    .Substring(1)
                    .Split('>')[0];

Similar implementation that supports normal and async methods is described in stackoverflow.com/questions/2968352/…
P
Pang

How about this:

StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name

MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name

P
Paul Williams

To handle both async and plain old method calls, I did this.

In my application, it's only getting called from exception handlers, so perf is not a concern.

[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentMethodName()
{
    var st = new StackTrace();
    var sf = st.GetFrame(1);
    string name = sf.GetMethod().Name;

    if (name.Equals("MoveNext"))
    {
        // We're inside an async method
        name = sf.GetMethod().ReflectedType.Name
                 .Split(new char[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries)[0];
    }

    return name;
}

C
Chef Gladiator

A bit more resilient, solution for customers from 2021,2022:

namespace my {
   public struct notmacros
   {

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string 
  whoami( [CallerMemberName] string caller_name = null)
  {
     if (string.IsNullOrEmpty(caller_name)) 
        return "unknown";
     if (string.IsNullOrWhiteSpace(caller_name)) 
        return "unknown";
     return caller_name;
  }
 }
} // my namespace

Usage

using static my.notmacros
 // somewhere  appropriate
 var my_name = whoami() ;

.NET fiddle link for the actual demo:

https://dotnetfiddle.net/moK73n

Please note the compiler requirement: .NET 6


C
Community

I think you should be able to get that from creating a StackTrace. Or, as @edg and @Lars Mæhlum mention, MethodBase.GetCurrentMethod()


m
mr R
using System;
                    
public class Program
{
    public static void Main()
    {
        
        Console.WriteLine("1: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
        OtherMethod();
    }
    
    public static void OtherMethod()
    {
        Console.WriteLine("2: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
    }
}

Output:

1: Main Program
2: OtherMethod Program

P
Pang

I just did this with a simple static class:

using System.Runtime.CompilerServices;
.
.
.
public static class MyMethodName
{
    public static string Show([CallerMemberName] string name = "")
    {
        return name;
    }
}

then in your code:

private void button1_Click(object sender, EventArgs e)
{
    textBox1.Text = MyMethodName.Show();
}

private void button2_Click(object sender, EventArgs e)
{
    textBox1.Text = MyMethodName.Show();
}

R
Romerik Rousseau

Add this method somewhere and call it without parameter!

public static string GetCurrentMethodName([System.Runtime.CompilerServices.CallerMemberName] string name = "")
{
    return name;
}

A
Adriano Silva Ribeiro

Try this...

    /// <summary>
    /// Return the full name of method
    /// </summary>
    /// <param name="obj">Class that calls this method (use Report(this))</param>
    /// <returns></returns>
    public string Report(object obj)
    {
        var reflectedType = new StackTrace().GetFrame(1).GetMethod().ReflectedType;
        if (reflectedType == null) return null;

        var i = reflectedType.FullName;
        var ii = new StackTrace().GetFrame(1).GetMethod().Name;

        return string.Concat(i, ".", ii);
    }

D
David Buck
new StackTrace().ToString().Split("\r\n",StringSplitOptions.RemoveEmptyEntries)[0].Replace("at ","").Trim()