本章開啟「C# to IL」核心篇章,通過大量對比範例,系統展示 C# 基礎語法結構如何編譯為 IL 指令序列。涵蓋:死碼消除、預設建構函數機制、命名空間合併、ref/out 統一為位址傳遞、字串連接與比較編譯期優化、字面量編碼、關鍵字逸出、註解剝離、字串轉義與逐字字串、預處理指令編譯期執行、繼承與虛方法調度、new/override/base 語義差異、介面實現與 .override。透過 IL 視角重構 C# 認知體系。
// C#: return; Console.WriteLine("hi");
// IL: br.s IL_0002; IL_0002: ret
// 編譯器發出警告,IL 中無 WriteLine 調用
.ctor() 呼叫 base.ctor()。
// C#: namespace vijay { namespace mukhi { class zzz } } }
// IL: .namespace vijay.mukhi { .class zzz ... }
// 多個同名命名空間在同一組件中會被編譯器合併
IL 僅支援單層完整命名空間,巢狀結構為語言糖衣。
// C#: void abc(ref int i) { i = 10; }
// IL: .method ... abc(int32& i) // 參數型別標記 &
// 呼叫端: ldloca.s V_0 (取址)
// 方法內: ldarg.1 (載入位址) → ldc.i4.s 10 → stind.i4 (間接存儲)
ldloca.s 載入區域變數位址;ldarg.1 載入參數位址。stind.i4 將值透過位址間接存儲。
// C#: "hi" + "bye" → IL: ldstr "hibye"
// C#: s + "bye" → IL: call string String::Concat(string, string)
// C#: a == b → IL: call bool String::Equals(string, string)
證明:字串運算為 C# 編譯器內建語義,IL 僅見方法調用。
// C#: (char)65 → IL: ldc.i4.s 65 → call WriteLine(wchar)
// C#: (char)i → IL: 變數型別為 wchar,調用時直接作為 wchar 傳遞
'A'、Unicode '\u0041'、十六進位 0x41 在 IL 均為 ldc.i4.s 65。
// C#: int @int; → IL: .locals (int32 V_0) 區域變數;.field private int32 'int' 實例欄位
// @ 符號編譯期移除,IL 中以單引號包裹保留字欄位名
單行 //、多行 /* */、XML 文檔註解 /// 均不生成任何 IL 指令或元數據(除非編譯器選項輸出 XML 文檔檔案)。
| C# 寫法 | IL 實際字串 | 說明 |
|---|---|---|
"hi\nBye\tNo" | hi<LF>Bye<TAB>No | 轉義序列編譯期展開 |
@\"hi\nBye\tNo\" | hi\nBye\tNo | 逐字字串:忽略轉義,換行實際換行 |
\"\"\"\" | " | 雙引號轉義 |
#define/#undef:條件編譯開關,決定哪些程式碼進入 IL。#if/#else/#endif:編譯期分支,僅保留真分支 IL。#warning:編譯器輸出警告,不影響 IL。#line/#error:除錯/錯誤指令,無可執行輸出。
// C#: class xxx : yyy
// IL: .class xxx extends yyy
call yyy::abc())。callvirt 運行期根據物件實際型別分派。| C# 修飾符 | IL 方法標記 | 調用指令 | 分派邏輯 |
|---|---|---|---|
| (無) | 無 | call | 靜態綁定至宣告類型 |
| virtual | virtual + newslot | callvirt | 運行期分派,新虛擬槽位 |
| override | virtual (無 newslot) | callvirt | 覆寫基類虛擬槽位 |
| new | 無 virtual | call | 隱藏基類同名方法,靜態綁定 |
// C#: base.abc();
// IL: ldarg.0; call instance void yyy::abc() // 直接呼叫基類實現,忽略虛擬分派
base 為編譯期關鍵字,IL 中無對應指令。
override 實現多態覆寫:基類 newslot virtual、派生類 virtual(無 newslot) → 運行期分派至派生類。
// C#: class yyy : iii { void abc() { } } // 介面 iii 有 pqr()
// IL: .class yyy implements iii
// .override iii::pqr with instance void yyy::abc()
允許介面方法與實現方法名稱不同,.override 建立映射。呼叫介面方法時實際調用映射的實現方法。
上篇確立了 C#→IL 映射的核心規律:
下篇將深入 const/readonly 細節、建構函數鏈、運算符降級、私有建構函數等進階主題。