在C#中登录时,如何获取调用当前方法的方法名称?我了解System.Reflection.MethodBase.GetCurrentMethod()
,但我希望在堆栈跟踪中再向下多走一步。我考虑过解析堆栈跟踪,但我希望找到一种更为明确的、更为简洁的方法,类似于Assembly.GetCallingAssembly()
但是适用于方法。
在C#中登录时,如何获取调用当前方法的方法名称?我了解System.Reflection.MethodBase.GetCurrentMethod()
,但我希望在堆栈跟踪中再向下多走一步。我考虑过解析堆栈跟踪,但我希望找到一种更为明确的、更为简洁的方法,类似于Assembly.GetCallingAssembly()
但是适用于方法。
试试这个:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name
在C# 5中,您可以使用调用方信息获取该信息:
//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}
你还可以获取[CallerFilePath]
和[CallerLineNumber]
。
[CallerTypeName]
在当前的 .Net Framework(4.6.2)和 Core CLR 中被删除了。 - Ph0en1xpublic static string WhoseThere([CallerMemberName] string memberName = "")
{
return memberName;
}
这个测试说明了这一点:
[Test]
public void Should_get_name_of_calling_method()
{
var methodName = CachingHelpers.WhoseThere();
Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}
虽然 StackTrace 在大多数情况下运行得相当快且不会影响性能,但 Caller Information 的速度仍然更快。在1000次迭代的示例中,我发现它要快40倍。
CachingHelpers.WhoseThere("wrong name!");
==> "wrong name!"
,因为 CallerMemberName
仅替换默认值。 - Olivier Jacot-Descombesthis
参数传递到扩展方法中。此外,Olivier 是正确的,你可以传递一个值,并且 [CallerMemberName]
不会被应用;相反,它作为一个覆盖功能,用于替代通常使用的默认值。事实上,如果我们查看 IL,我们可以看到生成的方法与对 [opt]
arg 发出的正常方法没有区别,因此注入 CallerMemberName
是 CLR 的行为。最后,文档中提到:“调用者信息属性[...] 影响省略参数时传递的默认值”。 - Shaun Wilsonasync
操作,这一点是StackFrame
无法帮助您的。此外,它不会影响从lambda函数中调用。 - Aaron两种方法的快速回顾,其中速度比较是重要的部分。
在编译时确定调用者
static void Log(object message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}
使用栈来确定调用者
static void Log(object message)
{
// frame 1, true for source info
StackFrame frame = new StackFrame(1, true);
var method = frame.GetMethod();
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber();
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}
两种方法的比较
Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms
所以你看,使用属性要快得多!实际上快了近25倍。我们可以仅实例化实际需要的帧,而非整个堆栈,从而在阿萨德先生的代码(目前被接受的答案)上进行改进:
new StackFrame(1).GetMethod().Name;
尽管很可能仍需要使用完整的堆栈来创建单个帧,但这样做可能会表现得更好。此外,它仍然具有Alex Lyman指出的相同注意事项(优化器/本机代码可能会破坏结果)。最后,您可能需要检查确保new StackFrame(1)
或.GetFrame(1)
不返回null
,尽管这种可能性似乎不太可能。
请参阅相关问题: 您可以使用反射找到当前正在执行的方法的名称吗?
new ClassName(…)
等于 null,这种情况真的可能吗? - Display NameSystem.Diagnostics.StackTrace
类来获取System.Diagnostics.StackFrame
,然后使用GetMethod()
方法获取System.Reflection.MethodBase
对象。但是,这种方法存在一些注意事项:
(注意:我只是在扩展Firas Assad提供的答案。)
从.NET 4.5开始,您可以使用Caller Information属性:
CallerFilePath
- 调用函数的源文件;CallerLineNumber
- 调用函数的代码行数;CallerMemberName
- 调用函数的成员。
public void WriteLine(
[CallerFilePath] string callerFilePath = "",
[CallerLineNumber] long callerLineNumber = 0,
[CallerMemberName] string callerMember= "")
{
Debug.WriteLine(
"Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}",
callerFilePath,
callerLineNumber,
callerMember);
}
这个功能也存在于“.NET Core”和“.NET Standard”中。
参考资料
显然这是一个晚回答,但如果你可以使用.NET 4.5或更高版本,我有一个更好的选择:
internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}
这将打印当前日期和时间,接着是"Namespace.ClassName.MethodName",最后以": text"结束。
示例输出:
6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized
使用示例:
Logger.WriteInformation<MainWindow>("MainWindow initialized");
CallerMemberName
必须作为参数定义。通常在日志方法的最后一个参数中传入params数组是很常见的,这使得将这两个东西结合起来变得困难。 - devklick/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
return GetCallingMethod("GetCallingMethod");
}
/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
string str = "";
try
{
StackTrace st = new StackTrace();
StackFrame[] frames = st.GetFrames();
for (int i = 0; i < st.FrameCount - 1; i++)
{
if (frames[i].GetMethod().Name.Equals(MethodAfter))
{
if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
{
str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
break;
}
}
}
}
catch (Exception) { ; }
return str;
}
StackTrace
,StackFrame
和CallerMemberName
)的基准测试,并将结果发布在此处的gist链接中,供其他人查看:https://gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1f。 - Shaun Wilson