C# 7.1 之前,Main 不支援 async 修飾詞,直接寫 async static void Main() { await ... } 會編譯失敗。本文示範三種繞過限制的標準寫法,讓主控台應用程式一進門就能優雅地 await。
await 需要外層方法標記 async,但編譯器生成的進入點簽名固定為 static void Main() 或 static int Main(),無法加 async。強行加上會得到 CS4009 錯誤:「入口點不能標記為 async」。
static void Main(string[] args) {
RunAsync().GetAwaiter().GetResult(); // 同步等待完成
}
static async Task RunAsync() {
await Task.Delay(3000);
Console.WriteLine("Done, Done");
}
關鍵:用 GetAwaiter().GetResult() 而非 .Result 或 .Wait(),可保留原始例外堆疊,避免 AggregateException 包裹。
static void Main(string[] args) {
Action act = async () => {
await Task.Delay(5000);
Console.WriteLine("哇哈哈,哈哈哇。");
};
act(); // 啟動非同步
Console.Read(); // 防止主執行緒結束
}
委派本身可標記 async,呼叫 act() 即啟動狀態機,主執行緒繼續往下跑,靠 Console.Read() 擋住結束。
static async Task Main(string[] args) {
await Task.Delay(8000);
Console.WriteLine("新大陸發現!");
}
編譯器會自動生成同步包裝器,開發者完全感知不到。
Main 用 .Result/.Wait() 同步等待 → 易造成死結,特別是 UI 執行緒或 ASP.NET 同步上下文。Console.Read、Task.WaitAll、GetAwaiter().GetResult())等待完成,否則程式瞬間退出。async Task<int> Main() 可直接回傳 Exit Code,方便腳本判斷成敗。現在,Main 也能寫出現代化的非同步程式碼了——選適合專案框架版本的姿勢,告別 Thread.Sleep 與 ManualResetEvent 的遠古時代吧。