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

《C# to IL》第二章 IL 基礎:從 Hello World 到堆疊機、型別系統、物件模型、裝箱拆箱、呼叫約定

     編輯:關於C#

前言

本章以最小化 C# 程式為切入點,透過 ildasm 反組譯與 ilasm 重組譯,逐行解讀 IL 核心結構:組件宣告、類別定義、方法簽名、堆疊機運作、區域變數、欄位初始化、建構函數鏈、靜態建構函數、新物件創建、this 指標、值型別/參考型別、裝箱/拆箱、參數傳遞、返回值處理。建立「IL 視角」認知 C# 運行時機制的基石。

一、 最小化範例:Hello World 解剖


// C#:
class zzz { static void Main() { WriteLine("hi"); abc(); } static void abc() { WriteLine("bye"); } }

// 精簡 IL (移除元數據、偽指令):
.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
    .method public hidebysig static void vijay() cil managed
    {
        .entrypoint
        ldstr "hi"
        call void [mscorlib]System.Console::WriteLine(string)
        call void zzz::abc()
        ret
    }
    .method public hidebysig static void abc() cil managed
    {
        ldstr "bye"
        call void [mscorlib]System.Console::WriteLine(string)
        ret
    }
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        ldarg.0
        call instance void [mscorlib]System.Object::.ctor()
        ret
    }
}

關鍵要素對照表

IL 元素含義C# 對應
.assembly組件宣告專案屬性/AssemblyInfo
.class private auto ansi私有、自動佈局、ANSI 字串class zzz (預設 internal)
extends System.Object繼承基類隱式繼承 object
.method ... vijay()方法定義static void Main()
.entrypoint程式進入點Main 方法特徵
ldstr載入字串常量至堆疊"hi"
call靜態/非虛方法調用WriteLine(), abc()
ret返回方法結束
.ctor實例建構函數隱式無參建構函數
ldarg.0載入第0參數 (this)建構函數隱式 this

二、 靜態建構函數 .cctor:優於一切的初始化


// C#: static zzz() { WriteLine("bye"); }
// IL: .method private hidebysig specialname rtspecialname static void .cctor() { ... }
// 輸出順序: bye → hi (靜態建構函數優於 .entrypoint 執行)
  • .cctor = 靜態建構函數,specialname rtspecialname 標記運行時識別。
  • 執行時機:類型首次被存取前 (靜態欄位讀寫、建立實例、呼叫靜態方法)。
  • 不呼叫基類 .cctor,無 this 指標。

三、 實例建構與 newobj:this 指標與基類鏈


// C#: new zzz();
// IL: newobj instance void zzz::.ctor(); pop (丟棄堆疊上的物件引用)
  • newobj:分配記憶體 → 呼叫 .ctor → 將物件引用壓入堆疊。
  • ldarg.0 = this:實例方法隱式第一參數,指向物件欄位記憶體位置。
  • 基類 .ctor 必須顯式 call,且最先執行。
  • 多個實例共享同一方法碼,各自擁有獨立欄位記憶體。

四、 區域變數與堆疊機:.locals、ldc、stloc、ldloc


// C#: int i = 6; long j = 7;
// IL:
.locals (int32 V_0, int64 V_1)  // 宣告區域變數槽位
ldc.i4.6; stloc.0               // int32 常量 6 入棧 → 存入 V_0
ldc.i4.7; conv.i8; stloc.1      // int32 7 入棧 → 擴展為 int64 → 存入 V_1
  • 變數名稱編譯期丟失,IL 使用 V_0, V_1... 槽位索引。
  • ldc.i4 系列:常量入棧,.s 後綴 = 1 位元組編碼 (短形式)。
  • conv.i8:型別轉換指令 (int32 → int64),32 位機器記憶體對齊需求。
  • stloc/ldloc:堆疊頂值 ↔ 區域變數槽位雙向傳輸。

五、 靜態/實例欄位:stsfld/ldsfld vs stfld/ldfld


// 靜態欄位初始化 (在 .cctor):
ldc.i4.6; stsfld int32 zzz::i

// 實例欄位初始化 (在 .ctor):
ldarg.0; ldc.i4.7; conv.i8; stfld int64 zzz::j
指令目標堆疊操作
stsfld/ldsfld靜態欄位值 ↔ 堆疊
stfld/ldfld實例欄位需先載入物件引用 (this)
  • 欄位名稱語法:類別名::欄位名 (靜態) / 物件引用 欄位名 (實例)。
  • 初始化順序:欄位歸零 → 顯式初始化賦值 → 建構函數程式碼。

六、 字串格式化與裝箱:box 指令核心機制


// C#: WriteLine("{0}", 20);
// IL:
ldstr "{0}"
ldc.i4.s 20
stloc.0
ldloca.s V_0          // 取區域變數位址
box [mscorlib]System.Int32  // 值型別 → 參考型別 (裝箱)
call WriteLine(string, object)

裝箱三步驟

  1. 在托管堆分配物件記憶體 (含型別資訊、同步索引、欄位值)。
  2. 將值型別資料複製至新物件欄位。
  3. 將新物件參考 (指標) 壓入堆疊。
  • ldloca.s 載入位址 (managed pointer),box 需要位址而非值。
  • 堆疊上值型別無物理位址,必須先存入區域變數槽位再取址。
  • 拆箱 (unbox) 由 WriteLine 內部處理,取出值型別資料。

靜態欄位裝箱


ldsflda int32 zzz::i  // 直接取靜態欄位位址
box [mscorlib]System.Int32

靜態欄位有固定位址,無需區域變數中轉。

七、 未初始化變數:C# 編譯期錯誤 vs IL 運行期垃圾值


// IL 僅 .locals 宣告,無 stloc:
.locals (int32 V_0)
ldloc.0; call WriteLine(int32)  // 輸出隨機記憶體殘留值 (如 51380288)
  • CLR 不強制區域變數初始化,C# 編譯器強制 (CS0165)。
  • IL 寬鬆需求 → 程式設計師需極度謹慎。

八、 參數傳遞與返回值:堆疊雙重角色


// 實例方法帶參數:
ldloc.0 (this); ldc.i4.s 10; call instance void abc(int32)

// 靜態方法返回值:
call int32 abc()  // 返回值壓入堆疊
stloc.0           // 存入區域變數

// newobj 亦返回物件引用至堆疊
  • 堆疊 = 引數傳遞通道 + 返回值通道。
  • 實例方法隱式 this 為第一參數 (ldarg.0)。
  • ldarg 載入參數值,ldarga 載入參數位址 (ref/out)。

九、 實例欄位存取返回值:stfld/ldfld 非互逆


// C#: a.i = abc(); WriteLine(a.i);
// IL:
call int32 abc()      // 返回值入棧
stfld int32 zzz::i    // 存入實例欄位 (需 this 在棧下)
ldloc.0 (this); ldfld int32 zzz::i  // 讀取欄位值入棧
call WriteLine(int32)
  • stfld 消耗堆疊頂值 + 物件引用。
  • ldfld 載入物件引用 → 壓入欄位值 (非移除引用)。
  • 靜態方法 abc() 不接收 this,但呼叫端仍需保留 this 以便後續 stfld。

十、 總結:IL 核心認知圖譜

  1. 堆疊機:所有運算、傳遞、返回的核心數據結構 (LIFO)。
  2. 型別系統:值型別 (棧/內嵌) vs 參考型別 (堆/指標),box/unbox 橋接。
  3. 物件模型:方法碼共享、欄位獨立、this 隱式傳遞、建構函數鏈強制基類優先。
  4. 初始化語義:零初始化 → 顯式賦值 → 建構函數邏輯;靜態 .cctor 優於實例 .ctor。
  5. 呼叫約定:call (靜態/非虛) vs callvirt (虛/介面),參數左到右入棧 (文檔) / 右到左 (實際)。
  6. 元數據驅動:型別、方法、欄位皆有完整元數據,支援反射、序列化、動態載入。

掌握本章內容,你已具備「讀懂 IL、理解 C# 底層行為、診斷疑難雜症」的核心能力。下章進入控制流 (選擇/迴圈) 與操作符映射篇。

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