首页 文章

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

提问于
浏览
407

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

17 回答

  • 59

    试试这个:

    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#] .

  • 57

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

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

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

  • 16

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

    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的工作速度非常快,但在大多数情况下都不会出现性能问题,但Caller Information的速度要快得多 . 在1000次迭代的样本中,我将其计时速度提高了40倍 .

  • 13

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

    • 它表示 runtime 堆栈 - 优化可以内联一个方法,你将 not 在堆栈跟踪中看到该方法 .

    • 它会 not 显示任何原生帧,所以如果你的方法有可能被本机方法调用,这将是 not 工作,实际上目前没有可行的方法 .

    (注意:我正在扩展由Firas Assad提供的the answer . )

  • 0

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

    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?

  • 8

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

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

    Determining the caller at compile-time

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

    Determining the caller using the stack

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

    Comparison of the 2 approaches

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

    所以你看,使用属性要快得多!事实上快了近25倍 .

  • 6

    从.NET 4.5开始,您可以使用Caller Information属性:

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

    References

  • 4

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

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

  • 2
    /// <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;
    }
    
  • 52

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

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

    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
    
  • 8
    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/

  • 277

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

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

  • -1

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

  • 413

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

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

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

    你想找到它的来电者 .

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

    public void MethodA(Action helperAction)
            {
                /*
                 * Method code here
                 */
            }
    
    1. Lambda名称不是随机生成的 . 规则似乎是:> <CallerMethodName> __ 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);
    
  • 1
    StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
    string methodName = caller.GetMethod().Name;
    

    我想,这就足够了 .

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

相关问题