最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

.NET中獲取調(diào)用方信息的方式CallerMemberName與StackTrace對(duì)比詳解

 更新時(shí)間:2026年05月22日 08:55:32   作者:YahirQ  
本文詳細(xì)介紹了CallerMemberName和StackTrace兩種獲取調(diào)用方信息的方式,從本質(zhì)、工作機(jī)制、性能表現(xiàn)、信息豐富度、對(duì)編譯優(yōu)化的敏感度、異步與多線環(huán)境支持度等方面進(jìn)行對(duì)比,需要的朋友可以參考下

在 .NET 開發(fā)中,我們經(jīng)常需要獲取“當(dāng)前方法被誰調(diào)用”這一信息——比如實(shí)現(xiàn) INotifyPropertyChanged 時(shí)自動(dòng)填充屬性名,或者在日志中記錄調(diào)用源。通常有兩種方式:編譯時(shí)特性 CallerMemberName 和運(yùn)行時(shí)類 StackTrace。雖然它們都能幫助我們追溯到調(diào)用方,但底層原理、性能表現(xiàn)和適用場(chǎng)景截然不同。本文將詳細(xì)剖析二者的差異,并給出實(shí)際開發(fā)中的選型建議。

1. 基本概念

類型CallerMemberNameStackTrace
命名空間System.Runtime.CompilerServicesSystem.Diagnostics
本質(zhì)特性(Attribute),用于方法參數(shù)類(Class),提供屬性和方法
獲取時(shí)機(jī)編譯時(shí)靜態(tài)填充運(yùn)行時(shí)動(dòng)態(tài)遍歷堆棧幀
返回信息僅調(diào)用成員的名稱(字符串)完整的調(diào)用堆棧(類名、方法名、文件名、行號(hào)等)

2. 工作機(jī)制

2.1 CallerMemberName:編譯時(shí)的智能替換

CallerMemberName 是編譯器“魔法”的一種。當(dāng)你在方法的某個(gè)參數(shù)上標(biāo)記 [CallerMemberName] 時(shí),編譯器會(huì)在調(diào)用點(diǎn)自動(dòng)將調(diào)用該方法的成員名稱以字符串字面量的形式傳入。整個(gè)過程發(fā)生在編譯階段,沒有任何運(yùn)行時(shí)開銷。

void Log(string message, [CallerMemberName] string member = "")
{
    Console.WriteLine($"{member}: {message}");
}
void Test() => Log("Hello"); 
// 編譯后相當(dāng)于 Log("Hello", "Test");

如果調(diào)用方是方法、屬性、構(gòu)造函數(shù)、事件等,傳入對(duì)應(yīng)的名稱;若調(diào)用方是頂級(jí)代碼(如 Main 或全局語句),則傳入空字符串 ""。

2.2 StackTrace:運(yùn)行時(shí)的堆??煺?/h3>

StackTrace 會(huì)在運(yùn)行時(shí)捕獲當(dāng)前線程的調(diào)用堆棧,通過遍歷每一幀(Frame)獲取方法信息(MethodBase),包括方法名、參數(shù)類型、返回類型、模塊名,甚至可以通過調(diào)試符號(hào)(.pdb)獲取源文件名和行號(hào)。

void Log(string message)
{
    var stackTrace = new StackTrace();
    var frame = stackTrace.GetFrame(1);          // 跳過Log方法本身
    var method = frame.GetMethod();
    Console.WriteLine($"{method.Name}: {message}");
}

StackTrace 的功能遠(yuǎn)不止獲取直接調(diào)用者——它可以逐幀向上回溯,構(gòu)建完整的調(diào)用鏈。

3. 核心差異詳解

3.1 性能對(duì)比

方案性能特點(diǎn)
CallerMemberName極高,零額外運(yùn)行時(shí)開銷(編譯時(shí)已確定)
StackTrace較低,需要遍歷堆棧幀、反射獲取方法信息,通常慢數(shù)十倍甚至上百倍

在實(shí)際基準(zhǔn)測(cè)試中,CallerMemberName 每秒可執(zhí)行數(shù)千萬次,而 StackTrace 僅數(shù)十萬次。因此在高頻調(diào)用的場(chǎng)景(如屬性變更通知)中,必須首選 CallerMemberName。

3.2 信息豐富度

  • CallerMemberName:只能返回一個(gè)簡(jiǎn)單的字符串——調(diào)用成員的名稱。例如 "OnPropertyChanged"。
  • StackTrace:可以返回調(diào)用方法的完整反射信息(MethodBase),進(jìn)而獲得:
    • 方法名稱、聲明類型、參數(shù)類型、返回類型
    • 模塊名稱、程序集信息
    • 文件名和行號(hào)(需 .pdb 文件支持)
    • 完整的調(diào)用棧(多幀)

3.3 對(duì)編譯器優(yōu)化的敏感度

  • CallerMemberName:不受 JIT 優(yōu)化影響,因?yàn)榫幾g器在編譯時(shí)已經(jīng)直接嵌入了字符串常量。
  • StackTrace:受 JIT 內(nèi)聯(lián)(Inlining)影響。如果一個(gè)方法被內(nèi)聯(lián)到調(diào)用者中,則它不會(huì)出現(xiàn)在堆棧幀中,導(dǎo)致 StackTrace 獲取不到該方法。解決方法是在需要精確堆棧的方法上應(yīng)用 [MethodImpl(MethodImplOptions.NoInlining)]。
[MethodImpl(MethodImplOptions.NoInlining)]
void MyMethod() { ... }   // 保證該方法一定有獨(dú)立的堆棧幀

3.4 異步與多線程環(huán)境

  • CallerMemberName:始終正常工作,因?yàn)樾畔⒃诰幾g時(shí)已固定。
  • StackTrace:在異步方法(async/await)中會(huì)遇到問題:編譯器會(huì)將異步方法改寫為狀態(tài)機(jī),await 之后的代碼可能運(yùn)行在不同的上下文中,傳統(tǒng)的 new StackTrace() 無法還原原始調(diào)用鏈。.NET 5 及更高版本在 Exception.StackTrace 中做了增強(qiáng),但直接使用 StackTrace 類依然不盡理想。

3.5 調(diào)用深度的支持

  • CallerMemberName:只能獲取直接調(diào)用者的名稱,無法向上遞歸。
  • StackTrace:可以獲取任意深度的調(diào)用鏈(通過 GetFrame(index) 循環(huán)遍歷)。

4. 代碼示例對(duì)比

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

public class CallerDemo
{
    public void CallWithCallerMember() => LogWithCaller("消息");

    public void CallWithStackTrace() => LogWithStackTrace("消息");

    private void LogWithCaller(string msg, [CallerMemberName] string member = "")
    {
        Console.WriteLine($"[CallerMemberName] 調(diào)用者: {member}, 內(nèi)容: {msg}");
    }

    private void LogWithStackTrace(string msg)
    {
        var stackTrace = new StackTrace();
        var callerFrame = stackTrace.GetFrame(1);
        var method = callerFrame.GetMethod();
        Console.WriteLine($"[StackTrace] 調(diào)用者: {method.Name}, 內(nèi)容: {msg}");
    }
}

// 輸出:
// [CallerMemberName] 調(diào)用者: CallWithCallerMember, 內(nèi)容: 消息
// [StackTrace] 調(diào)用者: CallWithStackTrace, 內(nèi)容: 消息

如果需要更詳細(xì)的堆棧信息(例如文件名和行號(hào)),可以啟用 fNeedFileInfo

var stackTrace = new StackTrace(true);   // 會(huì)嘗試獲取文件信息
var frame = stackTrace.GetFrame(1);
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber();

5. 適用場(chǎng)景與選型建議

場(chǎng)景推薦方案理由
實(shí)現(xiàn) INotifyPropertyChanged 的屬性變更通知CallerMemberName性能極致,避免硬編碼字符串
簡(jiǎn)單日志——只需記錄方法名CallerMemberName簡(jiǎn)潔、快速、零依賴
參數(shù)驗(yàn)證(如 ArgumentNullExceptionCallerMemberName自動(dòng)獲取被調(diào)用方法名
調(diào)試時(shí)查看完整調(diào)用堆棧StackTrace可獲得調(diào)用層次結(jié)構(gòu)
異常處理中記錄堆棧直接使用 Exception.StackTrace已包含完整信息,避免重復(fù)創(chuàng)建
性能分析 / 攔截器 / AOP 工具StackTrace需要豐富的調(diào)用上下文
動(dòng)態(tài)生成的代碼(如表達(dá)式樹、Emit)StackTrace(或 MethodBase.GetCurrentMethod()編譯時(shí)特性無法應(yīng)用于動(dòng)態(tài)成員

6. 注意事項(xiàng)與最佳實(shí)踐

  1. 不要在高頻路徑濫用 StackTrace:每次創(chuàng)建 StackTrace 對(duì)象都會(huì)造成可觀的內(nèi)存分配和 CPU 開銷。
  2. 避免在 async 方法中依賴傳統(tǒng) StackTrace 的準(zhǔn)確性:如需異步堆棧跟蹤,考慮使用 Activity 或第三方診斷庫。
  3. CallerMemberName 參數(shù)提供默認(rèn)值:這樣調(diào)用方可以省略實(shí)參,同時(shí)保證向后兼容。
void Log(string msg, [CallerMemberName] string member = "Unknown")
  1. 對(duì)于需要完整方法簽名(參數(shù)類型、泛型等)的場(chǎng)景,使用 StackTrace 結(jié)合 MethodBaseGetParameters() 等方法。
  2. 如果只需要當(dāng)前方法自身的名稱(而不是調(diào)用者),可以考慮 MethodBase.GetCurrentMethod().Name,但它依然有反射開銷。

7. 總結(jié)

維度CallerMemberNameStackTrace
性能極高較低
信息量僅成員名稱完整堆棧、類型、文件等
適用深度僅直接調(diào)用者任意深度調(diào)用鏈
運(yùn)行時(shí)依賴堆棧幀、反射、PDB(可選)
最佳場(chǎng)景高頻簡(jiǎn)單調(diào)用溯源低頻復(fù)雜調(diào)試/分析
  • 如果你的目標(biāo)是輕量、高頻地獲取調(diào)用者的名字(尤其是屬性通知、日志前綴),請(qǐng)選擇 CallerMemberName
  • 如果你需要完整調(diào)用上下文、文件位置或整個(gè)堆棧鏈條,并且性能不是第一瓶頸(如異常處理、診斷工具),請(qǐng)使用 StackTrace。

理解這兩種技術(shù)的本質(zhì)差異,可以幫助你在 .NET 開發(fā)中寫出更高效、更精準(zhǔn)的代碼。

以上就是.NET中獲取調(diào)用方信息的方式CallerMemberName與StackTrace對(duì)比詳解的詳細(xì)內(nèi)容,更多關(guān)于.NET獲取調(diào)用方信息的方式對(duì)比的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • gridview行索引獲取方法及實(shí)現(xiàn)代碼(非js版)

    gridview行索引獲取方法及實(shí)現(xiàn)代碼(非js版)

    前一篇是用Javascript獲取GridView的行索引,此篇Insus.NET使用非Javascript獲取GridView的行索引,感興趣的朋友可以了解下
    2013-01-01
  • Coolite Cool Study 2 同時(shí)更新多個(gè)Tab

    Coolite Cool Study 2 同時(shí)更新多個(gè)Tab

    前段時(shí)間有一個(gè)需求是這樣子的——錄入一個(gè)查詢條件, 出來的查詢結(jié)果是多張頁面。不知道有沒朋友遇到這個(gè)問題。 展現(xiàn)的效果大概是這個(gè)樣子
    2009-05-05
  • Asp.Net MVC4通過id更新表單內(nèi)容的思路詳解

    Asp.Net MVC4通過id更新表單內(nèi)容的思路詳解

    一個(gè)表單一旦創(chuàng)建完,其中大部分的字段便不可再編輯。只能編輯其中部分字段。下面通過本文給大家分享Asp.Net MVC4通過id更新表單內(nèi)容的思路詳解,需要的朋友參考下吧
    2017-07-07
  • ASP.NET Core3.1 Ocelot負(fù)載均衡的實(shí)現(xiàn)

    ASP.NET Core3.1 Ocelot負(fù)載均衡的實(shí)現(xiàn)

    這篇文章主要介紹了ASP.NET Core3.1 Ocelot負(fù)載均衡的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Asp.Net Core使用SignalR進(jìn)行服務(wù)間調(diào)用方法示例

    Asp.Net Core使用SignalR進(jìn)行服務(wù)間調(diào)用方法示例

    這篇文章主要介紹了Asp.Net Core使用SignalR進(jìn)行服務(wù)間調(diào)用方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12
  • .NET Core 實(shí)現(xiàn)微信小程序支付功能(統(tǒng)一下單)

    .NET Core 實(shí)現(xiàn)微信小程序支付功能(統(tǒng)一下單)

    最近公司研發(fā)了幾個(gè)電商小程序,還有一個(gè)核心的電商直播,只要是電商一般都會(huì)涉及到交易信息,離不開支付系統(tǒng),這里我們統(tǒng)一實(shí)現(xiàn)小程序的支付流程。感興趣的朋友跟隨小編一起看看吧
    2019-09-09
  • ASP.NET MVC5網(wǎng)站開發(fā)用戶修改資料和密碼(六)

    ASP.NET MVC5網(wǎng)站開發(fā)用戶修改資料和密碼(六)

    這篇文章主要介紹了ASP.NET MVC5網(wǎng)站開發(fā)用戶修改資料和密碼,本文即將結(jié)束member區(qū)域的用戶部分,感興趣的小伙伴們可以參考一下
    2015-09-09
  • .NET Runtime 是什么及主要功能

    .NET Runtime 是什么及主要功能

    .NET Runtime 是運(yùn)行 .NET 應(yīng)用程序的核心組件,它提供了應(yīng)用程序運(yùn)行所需的底層支持,比如內(nèi)存管理、類型系統(tǒng)、垃圾回收、線程管理等,這篇文章主要介紹了.NET Runtime 是什么,需要的朋友可以參考下
    2024-12-12
  • .NET Core 反射底層原理解析

    .NET Core 反射底層原理解析

    本文介紹了.NET中的反射機(jī)制,包括前期綁定(EarlyBinding)和后期綁定(LateBinding)的概念,反射是一種強(qiáng)大的工具,但在性能要求較高的場(chǎng)景中應(yīng)謹(jǐn)慎使用,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • 利用ASP.NET MVC和Bootstrap快速搭建響應(yīng)式個(gè)人博客站(一)

    利用ASP.NET MVC和Bootstrap快速搭建響應(yīng)式個(gè)人博客站(一)

    這篇文章主要介紹了利用ASP.NET MVC和Bootstrap快速搭建響應(yīng)式個(gè)人博客站(一)的相關(guān)資料,需要的朋友可以參考下
    2016-06-06

最新評(píng)論

额尔古纳市| 怀宁县| 建平县| 南宁市| 平陆县| 班玛县| 平罗县| 吴江市| 凌海市| 武义县| 东乡| 镶黄旗| 栖霞市| 城步| 巢湖市| 黄梅县| 辉县市| 修武县| 大足县| 天峨县| 紫金县| 山东省| 嘉祥县| 四平市| 巴彦淖尔市| 江北区| 龙泉市| 曲阳县| 理塘县| 利辛县| 宝坻区| 防城港市| 南陵县| 阜新| 铜鼓县| 运城市| 巴南区| 游戏| 邹城市| 油尖旺区| 永昌县|