本文透過同一份 C# 程式碼分別在 Windows(Microsoft CSC)與 Linux(Mono GMCS)編譯,並使用 ildasm 反組譯比較產出的 IL 代碼差異。發現 Mono 編譯器產出的 IL 更精簡,Microsoft 編譯器則包含許多看似多餘的指令(如 nop、initobj),且對結構體建構函式中 : this() 的處理截然不同。
Block.csusing System;
namespace Skyiv.Ben.Test
{
struct Block
{
public int Id { get; private set; }
public int Time { get; set; }
public Block(int id) : this() { Id = id; }
}
sealed class Test
{
static void Main()
{
Console.WriteLine(new Block(37).Time);
}
}
}
| 平台 | 編譯器 | 版本 |
|---|---|---|
| Windows | csc.exe | Microsoft (R) Visual C# 2008 Compiler 3.5.30729.1 |
| Linux (openSUSE 11.1) | gmcs | Mono C# compiler 2.4.0.0 |
兩組編譯產出的 .exe 在 Windows 與 Linux (Mono) 上執行皆輸出 0(Time 未初始化預設值),跨平台相容性良好。
instance 修飾詞Microsoft 編譯器產出的屬性簽章包含 instance,Mono 則無。此為語法糖差異,不影響執行。
get_Id 方法:Microsoft 產生大量多餘 ILnop、ldarg.0、call 等指令,約 20 行 IL。ldarg.0、ldflda、ldind.i4、ret 共 4 行。即使未加 /debug+,Microsoft 仍產生這些指令,推測為編譯器內部實作細節。
set_Id 方法:雙方完全一致此處兩編譯器產出相同 IL,顯示非所有情況 Microsoft 都較冗長。
.ctor(int):關鍵差異在 initobj原始碼:public Block(int id) : this() { Id = id; }
IL_0001: initobj Skyiv.Ben.Test.Block 呼叫無參數建構函式(對應 : this())。: this(),不產生 initobj,直接對欄位賦值。若移除 : this(),Microsoft 編譯報錯(CS0188、CS0843),強制要求呼叫預設建構函式以初始化自動屬性背後欄位;Mono 則編譯通過且執行正常。
作者在 Ubuntu MonoDevelop 開發 ACM 題目時未寫 : this(),Mono 編譯通過;提交至使用 Microsoft 編譯器的評測系統卻編譯失敗。這提醒跨平台開發時需注意編譯器語義差異。
nop、initobj。: this() 初始化自動屬性),但產生較多 IL 指令。