您可以使用反射来查找当前正在执行的方法的名称吗?


202

如标题所示:反射可以为您提供当前正在执行的方法的名称。

由于海森堡问题,我倾向于不猜测。您如何调用一个方法,该方法将告诉您当前方法而不更改当前方法是什么?但我希望有人能证明我做错了。

更新:

  • 第2部分:是否可以将其用于查找属性的内部代码?
  • 第3部分:演出效果如何?


我了解了MethodBase.GetCurrentMethod()的最终结果。我还了解到,不仅可以创建堆栈跟踪,还可以仅创建所需的确切帧。

要在属性中使用它,只需使用.Substring(4)删除“ set_”或“ get_”。


Joel,我知道这是一个老问题,但是通过创建方法的精确框架意味着什么?
Abhijeet

它指的是调用堆栈中的特定项目:重要的堆栈跟踪部分。
Joel Coehoorn 2013年

Answers:


119

从.NET 4.5开始,您还可以使用[CallerMemberName]

示例:一个属性设置器(用于回答第2部分):

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

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

编译器将在调用站点提供匹配的字符串文字,因此基本上没有性能开销。


3
这很棒!我正在使用StackFrame(1)其他答案中描述的方法进行日志记录,该方法似乎一直有效,直到Jitter决定开始内联事物为止。由于性能原因,我不想添加该属性来防止内联。使用该[CallerMemberName]方法解决了该问题。谢谢!
Brian Rogers

5
[CallerMemberName]在第4网上可用,打包
Venson

2
考虑到在调试模式下使用StackFrame(1)应该可以工作。但是在编译时使用发布模式时,可能会有一些优化,并且堆栈可能不是您期望的。
Axel O'Connell

这不会返回调用成员(即SomeProperty),而不是当前执行的方法吗?
Lennart

1
是的,调用二传手将导致呼叫OnPropertyChanged("SomeProperty")而不是OnPropertyChanged("SetProperty")
John Nilsson

189

对于非async方法,可以使用

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

https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.methodbase.getcurrentmethod

请记住,对于async方法,它将返回“ MoveNext”。


8
请注意,这并不总是能产生预期的结果。即,小版本方法或属性通常内嵌在发行版本中,在这种情况下,结果将改为调用者的方法名称。
亚伯2012年

5
据我所知,没有。因为在运行时中,MSIL从执行指针(它是JITted)不再可用。如果您知道方法的名称,则仍然可以使用反射。重点是,在内联时,当前正在执行的方法现在是另一种方法(即,堆栈中的一个或多个更高的方法)。换句话说,该方法消失了。即使您用NoInlining标记方法,也仍然有可能优化尾调用,在这种情况下,它也消失了。但是,在调试版本中它将起作用。
亚伯2012年

1
为了避免内联,请在方法顶部添加[MethodImpl(MethodImplOptions.NoInlining)]属性。
alex.peter '18

async方法内部,您很可能会获得“ MoveNext”作为方法名称。
维克多·亚雷玛

46

Lex提供的代码段有点长,所以我指出了重要的部分,因为没有其他人使用完全相同的技术:

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

这应该向MethodBase.GetCurrentMethod()。Name技术返回相同的结果,但是仍然值得指出,因为我可以在其自己的方法中使用上一个方法的索引1来实现一次,并从许多不同的属性中调用它。同样,它只返回一帧,而不是整个堆栈跟踪:

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

它也是单线的;)


可以在帮助器类中为公共静态字符串GetPropertyName()吗?静态方法?
Kiquenet 2011年

2
与Ed Guiness的答案相同:在内部版本或尾部调用优化的情况下,堆栈在发行版本中可能不同,并且第一种方法可能与当前方法不同。
亚伯

如果使用的是.Net 4.5,请参阅John Nilsson的答案,以较好地解决内联问题。
Brian Rogers

这可能比接受的答案好于上面的答案
-T.Todua

16

在一个空的控制台程序的Main方法中尝试以下操作:

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

控制台输出:
Main


12

当然是。

如果您想操纵一个对象,我实际上使用这样的函数:

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;
}

这行:

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

沿着堆栈框架走,以找到调用方法,然后我们使用反射来获取传递给它的参数信息值,以用于通用错误报告功能。要获取当前方法,只需使用当前堆栈帧(1)即可。

正如其他人对当前方法名称所说的,您还可以使用:

MethodBase.GetCurrentMethod()

我更喜欢遍历堆栈,因为如果从内部看该方法,无论如何它只会创建一个StackCrawlMark。在我看来,直接处理堆栈似乎更清晰

在4.5版之后,您现在可以将[CallerMemberNameAttribute]用作方法参数的一部分,以获取方法名称的字符串-这在某些情况下可能会有所帮助(但实际上在上述示例中)

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

这似乎主要是INotifyPropertyChanged支持的解决方案,以前您在整个事件代码中都乱扔了字符串。


不傻 我只是简单地将它们传递了出去。您可能可以做一些事情以使其看起来更简单,但是奖励比率的努力似乎倾向于保持简单。本质上,开发人员只需在方法签名的参数列表中复制(当然删除类型)。
Lex

它是什么:ExceptionFormat和GetParameterList?
Kiquenet'9

在答复中却很晚,但是:ExceptionFormat是一种恒定的字符串格式,而GetParameterList是一个简单的函数,该函数使用值格式化参数(您可以内联进行此操作)
Lex

11

比较获取方法名称的方法- 在LinqPad中使用任意定时构造

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 /programming/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;
    }
}

结果

反射 反射

堆栈跟踪 stacktrace

内联常数 常量内常量

不变 不变

expr e => e.expr()

专家 会员

callermember

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

请注意,exprcallermember方法并不十分“正确”。在那里您看到重复的相关评论,即反射比stacktrace快15倍。


9

编辑:MethodBase可能是一种更好的方法来获取您所在的方法(而不是整个调用堆栈)。不过,我仍然会担心内联。

您可以在方法中使用StackTrace:

StackTrace st = new StackTrace(true);

看一下框架:

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

但是,请注意,如果该方法是内联的,则不会进入您认为的方法之内。您可以使用属性来防止内联:

[MethodImpl(MethodImplOptions.NoInlining)]

由于Release优化而导致的内联特别棘手,因为代码在Debug和Release配置中的行为会有所不同。提防小型地产,它们是最有可能成为受害者的人。
DK。

我不知道你为什么要用new StackTrace(true)代替new StackTrace(false)。设置为true将会导致堆栈跟踪尝试捕获文件名,行号等,这可能会使调用速度变慢。否则,一个很好的答案
Ivaylo Slavov

6

处理的简单方法是:

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

如果在using块中包含System.Reflection:

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

4

这个怎么样:

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


0

试试这个...

    /// <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);
    }

0

我只是用一个简单的静态类来做到这一点:

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

然后在您的代码中:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.