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

时间:2008-10-05 13:28:42

标签: c# .net logging stack-trace system.diagnostics

登录C#时,如何学习调用当前方法的方法的名称?我知道关于System.Reflection.MethodBase.GetCurrentMethod()的所有内容,但我想在堆栈跟踪中向下迈出一步。我考虑过解析堆栈跟踪,但我希望找到一种更清晰,更明确的方法,比如Assembly.GetCallingAssembly(),但是对于方法。

19 个答案:

答案 0 :(得分:441)

试试这个:

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();

// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

来自 Get Calling Method using Reflection [C#]

答案 1 :(得分:302)

在C#5中,您可以使用来电信息获取该信息:

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

您还可以获得[CallerFilePath][CallerLineNumber]

答案 2 :(得分:98)

您可以使用来电者信息和可选参数:

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倍。

答案 3 :(得分:60)

通常,您可以使用System.Diagnostics.StackTrace类获取System.Diagnostics.StackFrame,然后使用GetMethod()方法获取System.Reflection.MethodBase对象。但是,这种方法有some caveats

  1. 它表示运行时堆栈 - 优化可以内联一个方法,您将 在堆栈跟踪中查看该方法。< / LI>
  2. 显示任何原生帧,因此如果本机方法有可能调用您的方法,则 不会 工作,实际上目前没有可行的方法。
  3. 注意:我只是扩展Firas Assad提供的the answer 。)

答案 4 :(得分:58)

我们可以通过仅实例化我们实际需要的帧而不是整个堆栈来改进阿萨德先生的代码(当前接受的答案):

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

这可能会有所改善,但很可能它仍然需要使用完整堆栈来创建该单帧。此外,它仍然有Alex Lyman指出的相同警告(优化器/本机代码可能会破坏结果)。最后,您可能需要检查以确保new StackFrame(1).GetFrame(1)不会返回null,这种可能性似乎不太可能。

请参阅此相关问题: Can you use reflection to find the name of the currently executing method?

答案 5 :(得分:54)

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

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倍   事实上更快。

答案 6 :(得分:25)

从.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”中。

<强>参考

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

答案 7 :(得分:13)

请注意,由于优化,在发布代码中执行此操作将不可靠。此外,以沙盒模式(网络共享)运行应用程序将不允许您抓取堆栈帧。

考虑aspect-oriented programming(AOP),例如PostSharp,而不是从您的代码中调用,修改您的代码,从而随时了解它的位置。

答案 8 :(得分:8)

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

答案 9 :(得分:8)

显然这是一个迟到的答案,但如果您可以使用.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");

答案 10 :(得分:6)

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

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

答案 11 :(得分:4)

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/

答案 12 :(得分:2)

我使用的另一种方法是向相关方法添加参数。例如,使用void Foo()而不是void Foo(string context)。然后传入一些指示调用上下文的唯一字符串。

如果您只需要调用者/上下文进行开发,则可以在发货前删除param

答案 13 :(得分:1)

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

就足够了。

答案 14 :(得分:1)

我们也可以使用lambda来查找调用者。

假设您有一个由您定义的方法:

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

你想找到它的来电者。

1 。更改方法签名,以便我们有一个Action类型的参数(Func也可以工作):

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

2 。 Lambda名称不是随机生成的。规则似乎是:&gt; &LT; CallerMethodName&GT; __ X 其中CallerMethodName被前一个函数替换,而X是一个索引。

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

3 。当我们调用MethodA时,必须由调用方法生成Action / Func参数。 例如:

MethodA(() => {});

4 。在MethodA中,我们现在可以调用上面定义的辅助函数,并找到调用方法的MethodInfo。

示例:

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

答案 15 :(得分:1)

查看 Logging method name in .NET 。小心在生产代码中使用它。 StackFrame可能不可靠......

答案 16 :(得分:1)

要获取方法名称和类名称,请尝试以下操作:

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

答案 17 :(得分:0)

Firas Assaad答案的其他信息。

我在.net core 2.1中使用了new StackFrame(1).GetMethod().Name;进行依赖项注入,并且将方法称为“开始”。

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

答案 18 :(得分:-1)

        <aspectLibraries>
           <aspectLibrary>
              <groupId>com.your.example.util</groupId>
              <artifactId>tracing-aspect</artifactId>
           </aspectLibrary>
        </aspectLibraries>