关于c#:如何找到调用当前方法的方法?

How can I find the method that called the current method?

当登录C时,我如何才能了解调用当前方法的方法的名称?我对System.Reflection.MethodBase.GetCurrentMethod()了如指掌,但我想在堆栈跟踪中比这低一步。我已经考虑过分析堆栈跟踪,但是我希望找到一种更明确的方法,比如Assembly.GetCallingAssembly(),但是对于方法。


试试这个:

1
2
3
4
5
6
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();

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

它来自使用反射的get调用方法。


在C 5中,您可以使用呼叫者信息获取该信息:

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

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


您可以使用呼叫者信息和可选参数:

1
2
3
4
public static string WhoseThere([CallerMemberName] string memberName ="")
{
       return memberName;
}

此测试说明了这一点:

1
2
3
4
5
6
[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倍。


一般来说,可以使用System.Diagnostics.StackTrace类获取System.Diagnostics.StackFrame,然后使用GetMethod()方法获取System.Reflection.MethodBase对象。但是,对于这种方法有一些警告:

  • 它表示运行时堆栈——优化可以内联一个方法,并且在堆栈跟踪中不会看到该方法。
  • 它不会显示任何本机帧,因此,如果您的方法有可能被本机方法调用,这将不起作用,而且实际上当前没有可用的方法来执行此操作。
  • (注:我只是在扩展Firas Assad提供的答案。)


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

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

    这可能会更好一点,尽管很可能它仍然需要使用完整的堆栈来创建那个单帧。此外,它仍然有亚历克斯莱曼指出的同样的警告(优化器/本机代码可能会破坏结果)。最后,您可能希望检查以确保new StackFrame(1).GetFrame(1)不会返回null,就像这种可能性看起来那样不太可能。

    请参阅此相关问题:可以使用反射来查找当前执行方法的名称吗?


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

    http://geekswithblogs.net/blackrabbitcoder/archive/2013/07/25/c.net-little-wounds-getting-caller-information.aspx

    在编译时确定调用方

    1
    2
    3
    4
    5
    6
    7
    8
    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);
    }

    使用堆栈确定调用方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);
    }

    两种方法的比较

    1
    2
    Time for 1,000,000 iterations with Attributes: 196 ms
    Time for 1,000,000 iterations with StackTrace: 5096 ms

    So you see, using the attributes is much, much faster! Nearly 25x
    faster in fact.


    从.NET 4.5开始,您可以使用调用方信息属性:

    • CallerFilePath—调用函数的源文件;
    • CallerLineNumber—调用函数的代码行;
    • CallerMemberName—调用函数的成员。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      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);
      }

    nbsp;

    此工具也存在于".net core"和".net standard"中。

    工具书类

  • Microsoft-呼叫者信息(C)
  • 微软-CallerFilePathAttribute
  • 微软-CallerLineNumberAttribute
  • 微软-CallerMemberNameAttribute

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

    考虑面向方面编程(AOP),比如PostSharp,它不是从代码中调用的,而是修改代码,从而随时知道代码在哪里。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /// <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;
    }


    显然这是一个迟来的答案,但如果您可以使用.NET 4.5或更高版本,我有更好的选择:

    1
    2
    3
    4
    internal static void WriteInformation<T>(string text, [CallerMemberName]string method ="")
    {
        Console.WriteLine(DateTime.Now.ToString() +" =>" + typeof(T).FullName +"." + method +":" + text);
    }

    这将打印当前日期和时间,后跟"namespace.classname.methodname",并以":text"结尾。样品输出:

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

    样品使用:

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

    也许你在找这样的东西:

    1
    2
    3
    4
    5
    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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    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/


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

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


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

    我想就够了。


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

    假设您定义了一个方法:

    1
    2
    3
    4
    5
    6
    public void MethodA()
        {
            /*
             * Method code here
             */

        }

    你想找到打电话的人。

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

    1
    2
    3
    4
    5
    6
    public void MethodA(Action helperAction)
            {
                /*
                 * Method code here
                 */

            }

    2。lambda名称不是随机生成的。规则似乎是:>uuux其中callermethodname被前一个函数替换,x是索引。

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

    三。当我们调用methoda时,action/func参数必须由调用方方法生成。例子:

    1
    MethodA(() => {});

    4。在methoda中,我们现在可以调用上面定义的helper函数并找到调用方方法的methodinfo。

    例子:

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

    看看.NET中的日志方法名。小心在生产代码中使用它。StackFrame可能不可靠…


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
        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回答的额外信息。

    我在.NET核心2.1中使用了new StackFrame(1).GetMethod().Name;和依赖注入,我将调用方法作为"start"。

    我试过用EDOCX1[1]它给了我正确的调用方法


    1
    2
    var callingMethod = new StackFrame(1, true).GetMethod();
    string source = callingMethod.ReflectedType.FullName +":" + callingMethod.Name;