從第一天開始,我一直追著自己的程式。
從 Program.cs 開始,到按下 Build,再到產生 DLL,最後甚至打開 DLL,看見 Compiler 產生的 IL。
昨天,我終於知道 DLL 裡存放的是 IL(Intermediate Language)。
我原本以為,故事到這裡就結束了,但是,今天當我再次看著 IL 時,突然想到一件事。
CPU 根本看不懂這些指令像是:
ldstr
call
ret
這些都不是 CPU 的機器碼。
如果 CPU 看不懂 IL,那我的程式到底是怎麼執行的?
以前,我一直以為按下 Build 之後,Compiler 就已經把 C# 完全變成 CPU 可以執行的程式。
直到昨天,我才知道Compiler 並不是直接產生 Machine Code。
它產生的是 IL。
今天,我想確認,既然 CPU 看不懂 IL,那中間是不是還少了一個步驟?
昨天,我的程式只有一行
Console.WriteLine("Day4");
今天,我把它改成:
Console.WriteLine("A");
Console.WriteLine("B");
Console.WriteLine("C");
重新 Build 後,再次使用 ILDasm 打開 DLL。

昨天看到的是:
ldstr "Day4"
call
ret
今天卻變成:
ldstr "A"
call
ldstr "B"
call
ldstr "C"
call
ret

這代表一件非常重要的事情,Compiler 並不是產生固定格式的 DLL。
而是根據我寫的每一行 C#,重新產生對應的 IL,只要我的程式改變,DLL 裡面的 IL 也會一起改變。
我可以確定我的程式最後變成了 IL。
但是,我也注意到IL 裡出現的是:
ldstr
call
ret
而不是:
101010101001...
CPU 真正能理解的,是 Machine Code。
也就是說 CPU 並不能直接執行 IL。


那麼,中間到底是誰負責最後一次翻譯?
查閱 Microsoft 官方文件後,我確認了整個執行流程。
C# 編譯器(Compiler)只負責把 C# 編譯成 IL,並存放到 DLL 中。
真正開始執行程式時,CLR(Common Language Runtime)會透過 JIT(Just-In-Time Compiler),在方法第一次被呼叫時,把 IL 編譯成目前 CPU 能執行的 Machine Code。
也就是說:
-Build 時:C# → IL
-執行時:IL → Machine Code
這兩個階段,分別由不同的 Compiler 完成。
這七天,我一直以為自己是在學 C#。
直到今天,我才發現,我真正理解的是,一個 C# 程式,究竟是如何一步一步變成 CPU 可以執行的內容。
從原始碼,到 Compiler,再到 DLL、IL、CLR、JIT,最後變成 Machine Code。
每一個角色都有自己的工作,而今天,我終於把這條流程完整拼起來了。
雖然目前的 Mini Web Framework 還只有幾行簡單的程式碼,但它已經完整經歷了和大型 .NET 專案相同的流程。
之後,不論我加入 Router、Middleware、HttpContext 或其他功能,它們都會先被 Compiler 編譯成 IL,再由 CLR 透過 JIT 編譯成 Machine Code,最後交給 CPU 執行。
理解這條流程,代表我不只是會使用 .NET,而是真正開始理解 .NET 的執行方式。