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

.NET 方法簽名深度解析:ref/out 與傳值的 IL 層面差異

     編輯:關於.NET

許多開發者知道 ref 和 out 代表「傳參考」,但對其與一般傳值(ByVal)在方法簽名、IL 指令、編譯期行為上的細微差異往往模糊不清。本文透過 IL 反組譯碼,從執行層面徹底釐清三者差異。

測試程式碼

static void Main()
{
    Int32 p;
    TestRef(out p);   // ① out 參數
    // TestRef(ref p); // ② ref 參數
    TestRef(p);       // ③ 傳值
}

static void TestRef(Int32 para)       { para = 1; } // ④ 傳值
static void TestRef(out Int32 para)   { para = 2; } // ⑤ out
// static void TestRef(ref Int32 para) { para = 3; } // ⑥ ref

IL 程式碼對照分析

Main 方法呼叫端

.method private hidebysig static void Main() cil managed
{
  .entrypoint
  .maxstack 1
  .locals init ([0] int32 p)
  L_0000: ldloca.s p          // 取區域變數 p 的位址 → 压栈
  L_0002: call void TestRef(int32&)  // 呼叫 out/ref 版本
  L_0007: ldloc.0             // 取區域變數 p 的值 → 壓栈
  L_0008: call void TestRef(int32)   // 呼叫傳值版本
  L_000d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  L_0012: pop
  L_0013: ret
}

關鍵差異:
ldloca.s (Load Local Address) → 傳遞位址(int32&)
ldloc.0 (Load Local Value) → 傳遞值副本(int32)

方法定義端簽名

方法IL 簽名關鍵標記
TestRef(Int32).method ... TestRef(int32 para)純值型別參數
TestRef(ref Int32).method ... TestRef(int32& para)& 表傳址
TestRef(out Int32).method ... TestRef([out] int32& para)[out] + &

ref 與 out 在 IL 層面皆為 int32&(傳址),唯一差別在 out 參數多一個 [out] 自訂屬性標記。

方法主體 IL 對比

// ref 版本 / out 版本(主體完全相同)
.method ... TestRef(int32& para)
{
  L_0000: ldarg.0          // 載入參數位址
  L_0001: ldc.i4.2         // 載入常數 2
  L_0002: stind.i4         // 將值存入位址指向的記憶體
  L_0003: ret
}

// 傳值版本
.method ... TestRef(int32 para)
{
  L_0000: ldc.i4.1         // 載入常數 1
  L_0001: starg.s para     // 存入參數槽(僅影響區域副本)
  L_0003: ret
}

stind.i4 (Store Indirect) 透過位址寫入記憶體,影響原始變數;
starg.s (Store Argument) 僅修改參數區域副本。

核心結論

  1. 呼叫端資料準備不同:ldloca.s 取址 vs ldloc 取值,對應「傳參考」與「傳值」。
  2. ref/out 無法構成多載:兩者 IL 簽名同為 int32&,僅 [out] 標記不同,CLR 視為相同簽名,編譯器禁止同時存在。
  3. 編譯期 vs 執行期語義:
    • 編譯期:ref 需預先初始化;out 不需初始化,但方法內必須指派值。
    • 執行期:兩者完全相同(皆為傳址),CLR 不區分。
  4. out 的設計意圖:表示「我不管初始值,只關心方法執行後的輸出狀態」(如 TryParse 模式),語意上屬「單向輸出參數」。

實務建議

  • 需修改呼叫端變數值 → 使用 ref(雙向)。
  • 單純輸出結果、不依賴初始值 → 使用 out(語意更清晰)。
  • 避免濫用 ref/out,優先考慮回傳值、Tuple、自訂結果型別,保持方法純粹性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved