iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0

前七天,我一路追著自己的程式往底層走。

從最開始寫下的 C#

Console.WriteLine("Hello");

一路追到 Compiler、DLL、IL、JIT,最後才真正理解一段 C# 程式是怎麼變成 CPU 可以執行的 Machine Code。

但當我準備開始打造自己的 Mini Web Framework 時,我突然發現了一個以前從來沒有認真想過的問題。

我的 Program.cs 明明只有:

Console.WriteLine("第一行");
Console.WriteLine("第二行");
Console.WriteLine("第三行");

沒有 class Program。

也沒有:

static void Main(string[] args)
{
}

可是按下執行後,程式卻知道要從第一行開始執行。

那麼問題來了,如果我根本沒有寫 Main(),程式到底從哪裡開始?

以前學 C# 時,我對 Main() 的印象一直是:Main() 就是程式開始的地方。

傳統的 C# 程式通常長這樣:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello");
    }
}

所以我一直很自然地認為:

啟動程式

找到 Main()

開始執行

但我的 Mini Web Framework 根本沒有這段程式。

它只有:

Console.WriteLine("第一行");
Console.WriteLine("第二行");
Console.WriteLine("第三行");

卻還是能正常執行。

沒有 Main(),真的能執行嗎?

我先把 Program.cs 寫成:

Console.WriteLine("第一行");
Console.WriteLine("第二行");
Console.WriteLine("第三行");

仔細檢查後,可以確定:

整個檔案裡完全沒有:

Main()

但 Build 成功,而且程式依然可以正常執行。

也就是說,我沒有自己寫 Main()並不代表程式沒有辦法找到開始執行的位置。

那這個位置到底在哪裡?
https://ithelp.ithome.com.tw/upload/images/20260809/20183395HPbiTLQUdj.png

Build 完成後,我打開:

MiniWebFramework.dll

接著找到 Program。

結果,我看到了一個很奇怪的東西:

點進去後,更重要的線索出現了:

.method private hidebysig static void '$'(string[] args) cil managed

而下面還有:

.entrypoint

https://ithelp.ithome.com.tw/upload/images/20260809/20183395jvDasUSh8z.png

這就奇怪了我的 C# 裡沒有 Main()

但是編譯完成的 DLL 裡卻出現了 $

而且它還被標示為:

.entrypoint

所以,我開始懷疑這個 Main 不是我寫的,而是 Compiler 幫我產生的。

查資料後,我才知道,原來我現在使用的是:

Top-level statements(頂層陳述式)

也就是我們可以直接寫:

Console.WriteLine("Hello");

而不必每次都寫:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello");
}
}

但這並不代表 Main() 的概念完全消失了。

更精確地說:

C# Compiler 會替 Top-level statements 產生程式的進入點。

所以原始碼看起來只有:

Console.WriteLine("Hello");

經過 Compiler 後,仍然會有一個讓程式開始執行的位置。

這也解釋了為什麼我會在 ILDasm 裡看到$

那如果我自己寫 Main() 呢?

既然 Compiler 可以替我處理,那我很好奇:

如果改回以前熟悉的寫法,結果會有什麼不同?

所以我把程式改成:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("第一行");
        Console.WriteLine("第二行");
        Console.WriteLine("第三行");
    }
}

重新 Build再用 ILDasm 打開 DLL。

https://ithelp.ithome.com.tw/upload/images/20260809/201833958CA3gPQUt9.png

這一次,原本的$真的變成了Main

而且裡面同樣出現: .entrypoint

兩次實驗放在一起,差異就變得非常明顯。

https://ithelp.ithome.com.tw/upload/images/20260809/20183395HmRXNAuCo9.png

我寫的 C# ILDasm:

Top-level statements

沒有自己寫 Main():

Compiler 產生 $

自己宣告 static void Main() 出現 Main

但它們有一個共同點:

.entrypoint

做到這裡,我才發現自己以前的理解其實少了一層。

以前我認為Main() = 程式的起點

但今天更精確的理解應該是可執行程式需要一個 Entry Point(進入點)。

Main() 是我們非常熟悉的一種呈現方式。

當我自己寫:

static void Main(string[] args)

它可以成為程式的 Entry Point。

而使用 Top-level statements 時,我雖然沒有親手宣告 Main(),Compiler 仍然會產生對應的入口。

今天最大的發現,不是 Top-level statements 可以少寫幾行程式碼。

而是我終於把:

Main()

和:

Entry Point

這兩個以前幾乎畫上等號的概念分開了,我以前看到 Main(),只知道,程式從這裡開始。

今天我第一次真正去追:

為什麼程式知道要從這裡開始?

甚至當我沒有自己寫 Main() 時,我還能透過 ILDasm 親眼看到 Compiler 替我產生的入口。

這讓 Program.cs 不再只是「每次建立專案都會自動出現的檔案」。我開始知道它在整個程式啟動流程裡扮演什麼角色。

這件事情對接下來的 Mini Web Framework 非常重要,因為未來我們會逐漸把:

Console.WriteLine("Hello");

變成真正的 Framework 啟動流程。

到時候,我們會需要:

Entry Point
     ↓
建立 Framework
     ↓
啟動 Server
     ↓
等待 Request
     ↓
處理 Request
     ↓
回傳 Response

也就是說,今天找到的 Entry Point,其實就是未來整個 Mini Web Framework 的第一站。

如果連程式從哪裡開始都不知道,就很難真正理解後面的 Web Server、Router、Middleware 到底是在什麼時候被建立、又是在什麼時候開始工作的。

現在,我已經知道程式從哪裡開始了。

但新的問題馬上出現。

我們目前寫的程式:

Console.WriteLine("第一行");
Console.WriteLine("第二行");
Console.WriteLine("第三行");

執行完第三行之後程式就結束了。

可是 Web Server 明明不一樣。

我打開一個網站時,Server 不可能:

啟動

執行三行程式

結束

它必須一直活著,一直等待新的 Request。

所以明天,我們要追下一個問題:

為什麼一般程式執行完就結束,但 Web Server 可以一直等 Request?

這會是我們第一次真正從「C# 程式怎麼執行」,走進:Web Framework 到底是怎麼運作的。


上一篇
如果 CPU 看不懂 IL,那程式到底怎麼執行?
下一篇
為什麼一般程式執行完就結束,但 Web Server 可以一直等 Request?
系列文
程式設計沒有告訴你的事:30 天破解每一個 Why17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言