在.NET 10中基於文件的C#執行
[[academy-video-youtube({"vid": "2i0MJDHvJq0", "start_time": "0", "title": "File-Based C# Execution in .NET 10", "creator": "Tim Corey", "length": "13m 36s"})]]
C# 一直需要專案文件來運行程式碼。 即使是最簡單的 "Hello World" 也需要一個 .csproj、一個 Program.cs,以及在執行任何操作之前的構建步驟。 使用 .NET 10,情況改變了。 現在,您可以編寫一個單一的 .cs 文件並直接運行它,就像執行 Python 腳本或 Node.js 文件一樣。
在他的影片 "File-Based C# Execution in .NET 10" 中,Tim Corey 介紹了這個功能的每個方面:運行獨立文件、傳遞命令行參數、內嵌新增 NuGet 套件、發布為本地可執行文件,並在範圍超出單一文件格式時轉換為完整專案。 如果您一直使用控制台應用程式來快速編寫腳本和原型,這將大大改變工作流程。
運行單一 C# 文件
[0:40 - 1:48] Tim 在 VS Code 開啟了一個空資料夾。 沒有解決方案,沒有專案文件,沒有樣板程式碼。 他建立了一個名為 demo.cs 的單一文件,其中包含一行程式碼:
Console.WriteLine("Hello World");Console.WriteLine("Hello World");要執行它:
dotnet run file demo.csdotnet run file demo.cs這就是完整的工作流程。 dotnet run file 命令在一個步驟中編譯並執行 .cs 文件。沒有生成中間 .csproj,也沒有建立 bin 或 obj 資料夾。 其心智模型更接近於腳本,與傳統的 C# 開發不同:編寫一個文件,運行它,查看輸出。
Tim 直接將其與 Python 和 JavaScript 進行比較,這兩者的單文件執行一直是預設行為。 .NET 10 將 C# 帶入了這一領域,同時保持了 C# 具有吸引力的型別安全性和性能。
命令行參數和隱式使用
[1:48 - 3:26] args 陣列自動可用,就像標準的 Program.cs 中有頂級語句一樣。 Tim 修改了文件以接受一個名稱參數:
Console.WriteLine($"Hello {args[0]}");Console.WriteLine($"Hello {args[0]}");用參數運行它:
dotnet run file demo.cs Timdotnet run file demo.cs Tim這將印出 "Hello Tim"。 Tim 指出,由於文件式執行預設包含隱式使用,Console.WriteLine 無需 using System; 語句即可運作。 這與 C# 9 中引入的頂級語句行為相同,現在已擴展到獨立文件。
微軟一直遵循的更廣泛模式是將繁瑣的程式逐步從 C# 中移除。全域使用、頂級語句以及現在的獨立 .cs 文件,都是讓 C# 適合於需要 Python 或 bash 腳本的快速任務的步驟。
新增使用者輸入
[3:26 - 5:43] Tim 用互動式輸入替換了參數方法,以展示文件式執行支持控制台應用程式模式的完整範圍:
Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");運行此程式時會提示輸入、讀取回應並印出問候語。 標準 I/O 與完整專案的運作方式相同。
Tim 指出的一個限制是:此模式嚴格限定為單一文件。您不能有兩個 .cs 文件相互引用。 如果您的程式碼增長到需要多個文件,是時候轉換為專案(影片後面有介紹)。
引用 NuGet 套件
[5:43 - 7:49] 這是單文件方法對腳本真正有用的地方。 您可以在 .cs 文件內部直接引用 NuGet 套件,使用一個 #r 指令在頂部:
#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;
Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;
Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");#r "nuget:..." 語法告訴運行時在編譯之前下載並引用指定的套件。 Tim 使用 Spectre.Console 為輸出著色,將問候語文字變為紅色。
不需要 dotnet add package 命令、無需編輯 .csproj,也不需要復原步驟。套件引用位於源文件本身中。 對於需要 HTTP 客戶端、JSON 序列化器或格式化程式庫的腳本來說,這消除了只為了引用一個依賴項而建立專案的額外負擔。
發布為本地可執行文件
[7:49 - 8:59] 當您要將文件式腳本作為獨立二進位發佈時,發布命令會處理這件事:
dotnet publish file demo.csdotnet publish file demo.cs這會在 artifacts 資料夾中生成一個可執行文件。 編譯的二進位包涵運行所需的一切,目標機器上不需要 .NET SDK。Tim 指出,文件式構建預設使用 Native AOT,這意味著輸出是一個單一的、自包含的二進位,具有快速啟動時間。
如果 Native AOT 導致與特定程式庫的相容性問題,您可以通過在文件頂部新增屬性指令來禁用它(類似於 #r 處理包的方式)。 然而,對於大多數腳本使用案例,AOT 是正確的預設選擇。
轉換為完整專案
[8:59 - 10:45] 當腳本超出單文件格式時的逃生門是轉換命令:
dotnet project convert file demo.csdotnet project convert file demo.cs這會生成一個 .csproj 文件,其中包含來自 #r 指令的NuGet 套件引用並將程式碼移到標準專案結構中。 從那時起,您可以新增多個文件、配置構建設置,並使用完整的 .NET 專案系統。
Tim 將此架構為一個自然的進程:從單一文件開始進行快速試驗,當範圍擴展時,提升為專案而無需重寫任何程式碼。 #r 指令可清晰轉換為生成的 .csproj 中的 <PackageReference> 條目。
Native AOT 和平台注意事項
[10:45 - 12:42] 預設情況下,文件式執行使用 Native AOT 編譯,產生最快的啟動時間。 Tim 表示,這對於冷啟動性能重要的 CLI 工具和腳本來說是理想選擇。 如果程式庫不相容 AOT,可以通過文件級屬性禁用該功能。
在 Linux 和 macOS 上,您還可以在 .cs 文件頂部新增一個 hashbang (#!/usr/bin/dotnet run file),使其直接從 shell 運行而無需輸入 dotnet run file 前綴。 這將 C# 完全帶入腳本領域,與 bash、Python 和 Ruby 並駕齊驅。
總結: C# 作為腳本語言
[12:42 - 13:05] Tim 突出的最有影響力的功能是消除了小任務的專案負擔。 控制台應用程式一直是快速實驗、自動化腳本和一次性工具的首選。 文件式執行去除了使這些感覺比需要更重的繁瑣程式,同時保留了選擇 C# 而不是腳本語言的每一個優勢(型別安全性、性能、NuGet 生態系統)。
結論
[13:05 - 13:36] 總結如下:.NET 10 的文件式執行允許您運行單一 .cs 文件與 dotnet run file,用 #r 指令引用 NuGet 套件,使用 dotnet publish file 發佈為本地二進位,當範圍超出單文件格式時,使用 dotnet project convert file 轉換為完整專案。 Native AOT 預設開啟,Linux/macOS 使用者可在 shell 層級得到 hashbang 支持。
對於任何您目前使用控制台應用程式進行原型或自動化的工作,這值得嘗試作為一種輕量選擇。
建議:如果您發現自己僅僅是為了測試 NuGet 套件或除錯 API 呼叫而建立控制台應用程式,請改為使用文件式執行。 建立一個 .cs 文件,在頂部新增 #r "nuget:PackageName, Version",編寫您的測試程式碼,並用 dotnet run file 運行它。 當您完成時,刪除該文件。無需進行專案清理。

