如何找到调用当前方法的方法?

当登录c#时,我如何才能知道调用当前方法的方法的名称?我知道关于System.Reflection.MethodBase.GetCurrentMethod()的所有信息,但我想在堆栈跟踪中再深入一步。我考虑过解析堆栈跟踪,但我希望找到一种更清晰、更显式的方式,类似于Assembly.GetCallingAssembly(),但用于方法。

326078 次浏览

试试这个:

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

它来自Get调用方法using Reflection [c#] .

注意,由于要进行优化,这样做在发布代码中是不可靠的。此外,在沙盒模式(网络共享)下运行应用程序根本不允许您获取堆栈帧。

考虑面向方面的编程 (AOP),就像PostSharp一样,它不是从代码中调用,而是修改代码,因此始终知道它的位置。

通常,您可以使用System.Diagnostics.StackTrace类来获得System.Diagnostics.StackFrame对象,然后使用GetMethod()方法来获得System.Reflection.MethodBase对象。然而,这种方法有一些警告:

  1. 它表示运行时堆栈——优化可以内联一个方法,您将<强> < / >强不在堆栈跟踪中看到该方法。
  2. 它将不< em > < / em >显示任何本机帧,所以如果有机会您的方法被本机方法调用,这将不< em > < / em >工作,实际上目前没有可用的方法来做它。

(# EYZ0)。

我们可以对阿萨德先生的代码(目前公认的答案)稍加改进,只实例化我们实际需要的帧而不是整个堆栈:

new StackFrame(1).GetMethod().Name;

这可能会执行得更好一些,尽管在所有的可能性中,它仍然需要使用整个堆栈来创建单个帧。此外,它仍然有Alex Lyman指出的同样的警告(优化器/本机代码可能会破坏结果)。最后,您可能希望检查new StackFrame(1).GetFrame(1)是否返回null,尽管这种可能性看起来不太可能。

参见相关问题: # EYZ0 < / p >

也许你正在寻找这样的东西:

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

我使用的另一种方法是向所讨论的方法添加参数。例如,使用void Foo(string context)代替void Foo()。然后传入一些表示调用上下文的惟一字符串。

如果您只需要用于开发的调用者/上下文,您可以在发布之前删除param

/// <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;
}
private static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}


private static Type GetCallingType()
{
return new StackFrame(2, false).GetMethod().DeclaringType;
}

这里有一个很棒的类:http://www.csharp411.com/c-get-calling-method/

在c# 5中,你可以使用调用者的信息来获取这些信息:

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}

你也可以得到[CallerFilePath][CallerLineNumber]

您可以使用Caller Information和可选参数:

public 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工作得相当快,在大多数情况下不会是一个性能问题,但调用者信息仍然要快得多。在1000次迭代的样本中,我发现它快了40倍。

StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

我想这就够了。

我们也可以用来找到调用者。

假设你有一个你定义的方法:

public void MethodA()
{
/*
* Method code here
*/
}

你想找到它的调用者。

# EYZ0。改变方法签名,这样我们就有了一个Action类型的参数(Func也可以):

public void MethodA(Action helperAction)
{
/*
* Method code here
*/
}
< p > # EYZ0。Lambda名称不是随机生成的。规则似乎是:> <CallerMethodName>__X 其中CallerMethodName被前面的函数替换,X是一个索引
private MethodInfo GetCallingMethodInfo(string funcName)
{
return GetType().GetMethod(
funcName.Substring(1,
funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
);
}
< p > # EYZ0。当我们调用MethodA时,Action/Func参数必须由调用方方法生成。 例子:< / p >
MethodA(() => {});

# EYZ0。在MethodA内部,我们现在可以调用上面定义的helper函数,并找到调用方方法的MethodInfo。

例子:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);

从。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);
    }
    

 

This facility is also present in ".NET Core" and ".NET Standard".

References

  1. Microsoft - Caller Information (C#)
  2. Microsoft - CallerFilePathAttribute Class
  3. Microsoft - CallerLineNumberAttribute Class
  4. Microsoft - CallerMemberNameAttribute Class

快速回顾一下这两种方法,速度比较是重要的部分。

http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx < a href = " http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx " > < / >

在编译时确定调用者

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 x

显然这是一个迟来的答案,但如果你能使用。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"”结尾。
样例输出:< / p >

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

示例使用:

Logger.WriteInformation<MainWindow>("MainWindow initialized");

要获得方法名和类名,请尝试以下方法:

    public static void Call()
{
StackTrace stackTrace = new StackTrace();


var methodName = stackTrace.GetFrame(1).GetMethod();
var className = methodName.DeclaringType.Name.ToString();


Console.WriteLine(methodName.Name + "*****" + className );
}

额外信息给Firas Assaad回答。

我已经使用了new StackFrame(1).GetMethod().Name;在。net核心2.1依赖注入和我得到调用方法为“开始”。

我试着用[System.Runtime.CompilerServices.CallerMemberName] string callerName = "" 它给出了正确的调用方法