首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 取得呼叫端方法名稱:StackTrace 反射實作與效能考量

     編輯:ASP.NET 基礎

應用場景

  • **記錄檔 (Logging)**:自動記錄「哪個方法呼叫了 LogHelper.Write()」。
  • **審計追蹤**:資料異動時記錄觸發業務方法。
  • **除錯輔助**:單元測試失敗時快速定位呼叫堆疊。

核心類別:System.Diagnostics.StackTrace

using System;
using System.Diagnostics;
using System.Reflection;

public static class CallerInfo
{
    /// 
    /// 取得呼叫端方法資訊 (跳過 1 層 = 直接呼叫者)
    /// 
    public static string GetCaller(int skipFrames = 1)
    {
        var st = new StackTrace(true); // true = 捕獲檔案/行號 (需 PDB)
        var frame = st.GetFrame(skipFrames); // 0=GetCaller, 1=直接呼叫者, 2=再上一層...
        if (frame == null) return "Unknown";

        var method = frame.GetMethod();
        var declaringType = method.DeclaringType;

        return $"Module: {method.Module.Name}\n" +
               $"Namespace: {declaringType?.Namespace}\n" +
               $"Class: {declaringType?.Name}\n" +
               $"Method: {method.Name}\n" +
               $"File: {frame.GetFileName()}:{frame.GetFileLineNumber()}";
    }
}

使用範例

public class OrderService
{
    public void CreateOrder(OrderDto dto)
    {
        // 業務邏輯...
        LogHelper.Info("訂單建立完成", CallerInfo.GetCaller());
    }
}

public static class LogHelper
{
    public static void Info(string message, string caller = "")
    {
        Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\nCaller:\n{caller}\n---");
    }
}

進階:.NET 4.5+ Caller Information Attributes (編譯期注入,零效能損耗)

using System.Runtime.CompilerServices;

public static class LogHelper
{
    public static void Info(string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
    {
        Console.WriteLine($"[{DateTime.Now}] {message} | Caller: {memberName} @ {filePath}:{lineNumber}");
    }
}

// 呼叫端完全不需傳參
public void CreateOrder() => LogHelper.Info("訂單建立"); 
// 輸出: Caller: CreateOrder @ D:\Proj\OrderService.cs:42

優勢:編譯器自動填入常數,**零執行期反射開銷**、支援內聯最佳化、**混淆後仍正確** (屬性值為字面常數)。

效能比較

方法相對耗時 (100 萬次)適用場景
`StackTrace` + 反射~ 1,200 ms舊版 .NET、需完整堆疊、動態跳過層數
`CallerMemberName` 等屬性~ 0 ms (編譯期)**新專案首選**、高頻 Logging、效能敏感
`MethodBase.GetCurrentMethod()`~ 300 ms僅需目前方法名,不需呼叫者

實務 Logging 架構建議

public static class Log
{
    // 內部使用 Caller 屬性,外部介面乾淨
    public static void Debug(string msg,
        [CallerMemberName] string member = "",
        [CallerFilePath] string file = "",
        [CallerLineNumber] int line = 0) => Write(LogLevel.Debug, msg, member, file, line);

    public static void Info(string msg,
        [CallerMemberName] string member = "",
        [CallerFilePath] string file = "",
        [CallerLineNumber] int line = 0) => Write(LogLevel.Info, msg, member, file, line);

    // 核心寫入 (可接入 Serilog/NLog/log4net)
    private static void Write(LogLevel level, string msg, string member, string file, int line)
    {
        var entry = new { Time = DateTime.UtcNow, Level = level, Message = msg, Caller = member, File = file, Line = line };
        // Serilog.Log.Write(level, "{@Entry}", entry); // 結構化日誌
        Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(entry));
    }
}

注意事項

  1. Release 建置需保留 PDB**:`StackTrace` 取得檔案/行號需 `.pdb` 檔案部署同目錄。
  2. 內聯最佳化會壓扁堆疊**:小方法可能被 JIT 內聯,導致 `StackTrace` 少一層。`Caller` 屬性不受影響。
  3. 非同步方法**:`async/await` 會切割堆疊,`StackTrace` 看到的是狀態機 `MoveNext`,`Caller` 屬性仍指向原始方法名。
  4. 安全性**:部分信任環境 (舊版沙箱) 禁止 `StackTrace`,需 `SecurityPermission(UnmanagedCode)`。

結語

取得呼叫端方法名稱,**現代 .NET (4.5+) 應優先使用 `CallerMemberName` 等編譯期屬性**,零效能成本、語法簡潔。僅在**舊版框架、需動態跳過層數、需完整堆疊字串**時才使用 `StackTrace` 反射。Logging 框架 (Serilog/NLog) 內建支援 `Caller` 屬性,建議直接接入成熟庫而非自行實作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved