在方法内部获取调用方法的名称

77

我有一个在对象中被多处调用的方法。有没有一种快速简便的方式来获取调用这个常用方法的方法名称。

伪代码示例:

public Main()
{
     PopularMethod();
}

public ButtonClick(object sender, EventArgs e)
{
     PopularMethod();
}

public Button2Click(object sender, EventArgs e)
{
     PopularMethod();
}

public void PopularMethod()
{
     //Get calling method name
}

PopularMethod()中,如果它是由Main调用的,我想看到Main的值...... 如果PopularMethod()是由ButtonClick调用的,我想看到"ButtonClick"

我看了一下System.Reflection.MethodBase.GetCurrentMethod(),但它无法获得调用方法。我也看了一下StackTrace类,但我真的不想在每次调用该方法时运行整个堆栈跟踪。

7个回答

209

在.NET 4.5 / C# 5中,这很简单:

public void PopularMethod([CallerMemberName] string caller = null)
{
     // look at caller
}

编译器会自动添加调用者的名称; 因此:

void Foo() {
    PopularMethod();
}

将传入"Foo"


6
这个解决方案的表现后果是什么? - Feckmore
10
没有任何东西:编译器在编译过程中会将其作为字面量添加。这比查看StackTrace等任何方式都要快得多。基本上,它被编译为PopularMethod(“CurrentMethodName”)。 - Marc Gravell
我遇到一个错误,提示找不到CallerMemberName。VS Express C# 2010。 - john k
@user396483,您需要针对.NET 4.5或更高版本进行目标定位才能存在该属性(尽管您可以自己定义),并且您需要使用C# 5才能以期望的方式工作。VS Express 2013可用且免费。 - Marc Gravell

82

我认为在不追踪堆栈的情况下无法完成。但是,这样做相当简单:

StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
Console.WriteLine(methodBase.Name); // e.g.

不过,我认为您真的必须停下来问自己这是否是必要的。


4
System.Diagnostics是StackTrace的命名空间,System.Reflection是MethodBase的命名空间。 - Guvante
1
Jason - 谢谢,我不确定“StackTrace”方法有多耗费资源,但这确实给了我我要找的东西。对我来说,这只是一个调试代码块,不会进入生产环境。再次感谢你的帮助! - user26901
2
@Scott Vercuski - 这是一项非常昂贵的操作,如果有任何避免这种情况的方法,你真的应该尽量避免。它比抛出异常还要糟糕,而抛出异常已经很糟糕了。虽然它可以做到,但不应滥用。 - John Leidegren
1
@John Leidegren - 当然...这不会进入生产代码,我们只是进行了一些大规模的QA测试并且出现了一些奇怪的情况,这只是为了帮助找出问题出在哪里。谢谢! - user26901
最近我需要为记录日志创建一个类。 我想在进行Log()调用时自动插入类名+方法名。 只有这个解决方案可以使用。 我建议使用for循环和StackTrace.GetFrame(i),并检查每个框架的方法名称(我排除了"log"和"debug",以便显示我要记录的实际方法)。 还要使用new StackTrace(2, false);,其中2是您的记录器类中的方法数量(您不需要记录),'false'设置为减少性能成本(如果您不需要文件信息)。 - Battle
显示剩余2条评论

17

这其实非常简单。

public void PopularMethod()
{
    var currentMethod = System.Reflection.MethodInfo
        .GetCurrentMethod(); // as MethodBase
}

但是要小心哦,我有点怀疑内联方法是否有效。您可以这样做以确保JIT编译器不会妨碍。

[System.Runtime.CompilerServices.MethodImpl(
 System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public void PopularMethod()
{
    var currentMethod = System.Reflection.MethodInfo
        .GetCurrentMethod();
}

获取调用方法:

[System.Runtime.CompilerServices.MethodImpl(
 System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public void PopularMethod()
{
    // 1 == skip frames, false = no file info
    var callingMethod = new System.Diagnostics.StackTrace(1, false)
         .GetFrame(0).GetMethod();
}

3
约翰·莱德格伦(John Leidgren) - 这将让我得到名为“PopularMethod”的方法名,但我想要调用PopularMethod的方法名。 - user26901
1
好的,那么你需要的是堆栈跟踪。我误读了你的问题。 - John Leidegren
只需要使用 .GetFrame(1) 就可以了,对吧? - Andrew
1
+1 对于 MethodInlining.NoInlining。顺便说一下,在 StackTrace .ctor 调用中的 false 是多余的,不是吗? - Ruben Bartelink
1
我相信如果我没有显式地请求文件信息,它的性能会稍微好一些。说实话,这可能并不重要,我这样做的主要原因是因为我不需要文件信息。 - John Leidegren

5

只需传入一个参数

public void PopularMethod(object sender)
{

}

个人认为:如果它足够适用于事件,那么它也应该足够适用于这个场景。


Sruly - 是的,那绝对是一个选项,我试图在不改变方法调用的情况下完成它。这将是我的最后一招选项。 - user26901
除非您将其公开为公共API,否则如果可以通过简单的方式完成,为什么要费力地使用反射呢?记住KISS原则。 - Sruly
这是最佳答案,不要使用反射或其他东西,通过参数识别调用者。 - Allen Rice
如果你正在寻找类似命名空间/程序集信息的东西,传递一个Type可能是更好的选择,因为调用可能来自静态方法。 - Chris Moschini
3
我已经为许多系统编写了代码。开发人员经常想要跟踪写入日志条目的方法。如果记录代码需要手动传递方法名,那么每次调用记录方法时,开发人员都需要输入方法名作为文本。现在在程序的许多不同部分中使用同一个记录方法,并查看随着多个开发人员在几年内添加、删除和重命名方法而发生的情况。自动化传递调用者的方法名称将极大地简化开发和调试。 - Zarepheth

5

我经常发现自己想要做到这一点,但最终总是重构系统设计,以避免出现“狗摇尾巴”反模式。结果通常是更强大的架构。


1

虽然你可以追踪堆栈并通过该方式找出问题,但我建议重新考虑你的设计。如果你的方法需要知道某种“状态”,我建议只需创建一个枚举或类似对象,并将其作为PopularMethod()的参数。根据你发布的内容,我认为跟踪堆栈是过度的。


0

我认为你需要使用StackTrace类,然后在下一帧上使用StackFrame.GetMethod()

虽然这似乎是一个奇怪的用途,但这确实需要使用Reflection。如果你正在定义PopularMethod,难道不能定义一个参数或其他东西来传递你真正想要的信息吗?(或者将其放在基类上...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接