你好 .NET run file,再見 csproj

你好 .NET run file,再見 csproj

這篇文章介紹了.NET CLI的檔案式程式新特性,它允許開發者直接執行C#原始檔而無需建立專案檔。該特性透過在記憶體中產生虛擬專案檔,並支援NuGet依賴套件和專案屬性設定,為開發指令碼和簡單應用提供了便捷。文章還展望了該特性的未來發展方向,包括目標路徑擴充、統一命令列參數、效能提升和更多檔案式程式命令支援等。

最後更新 2025/5/24 下午9:34
WeihanLi amazingdotnet
預計閱讀 7 分鐘
分類
.NET
標籤
.NET C# NuGet 指令碼

簡介

.NET 不斷進化,致力於讓開發體驗更簡單高效。最近,.NET CLI(命令列工具)提出了一項令人期待的新特性:可以直接執行 C# 原始檔,無需建立專案檔。這個特性稱為檔案式程式(File-based Programs),在 .NET 10 中將引入 dotnet run file 的支援,支援 dotnet sdk 直接執行,目前在 .NET 10 Preview 4 中已經可用,大家可以下載最新的 .NET 10 SDK 來試試看,之前自己動手寫的 dotnet-exec 部分功能可以使用原生的 SDK 支援了

什麼是檔案式程式

傳統 .NET 應用開發都需要一個專案檔(.csproj),哪怕只是執行一段簡單程式碼。檔案式程式則打破了這個門檻,允許你直接用命令列執行任意 C# 檔案:

echo 'Console.WriteLine("Hello C#!");' > hello.cs
dotnet run hello.cs

無需專案檔,無需複雜設定,像寫腳本一樣用 C#!

dotnet run hello.cs

運作原理

  • 當你執行 dotnet run file.cs 時,CLI 會自動在記憶體中為這個檔案產生一個「隱含專案」(即虛擬專案檔),效果等同於基於 dotnet new console 範本建立的專案,這保證了你用單檔案腳本時,和用標準專案開發的行為一致,它的實作方式和 dotnet-exec 最近的 project compiler 很相似,不過實作得更好一些,實作的是虛擬專案檔,並未真實地建立檔案。
  • 目標檔案下的所有 .cs 檔案(包括子目錄)都會參與編譯(例如公共的工具類、資源等)。
  • 相關的建構檔(如 Directory.Build.props)同樣會被自動套用。
  • 如果你執行的檔案沒有進入點方法,會有錯誤提示,避免誤操作,示例如下:

dotnet run file error

更多用法

  • 為了支援檔案程式的擴充,新增了 #: 指令支援,在編譯專案檔時會自動忽略,針對檔案程式可以解析轉成專案檔裡的 sdk、引用和 project 屬性設定

  • 透過檔案頂部的特殊指令(如 #:sdk Microsoft.NET.Sdk.Web#:package#:property),你可以宣告 NuGet 相依套件或專案屬性。例如:

#:sdk Microsoft.NET.Sdk.Web #:package System.CommandLine@2.0.0-* #:property TargetFramework net10.0


我們可以指定 `#:sdk Microsoft.NET.Sdk.Web` 來建立一個單檔案的 WebApplication,

```csharp
#:sdk Microsoft.NET.Sdk.Web

var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
await app.RunAsync();

透過 dotnet run webapi.cs 執行我們的單檔案 webapi

dotnet run webapi.cs

存取我們的 api

webapi test

我們也可以引用 nuget package 和設定 property,示例如下:

#:sdk Microsoft.NET.Sdk.Web
#:package WeihanLi.Web.Extensions@2.1.0
#:property ManagePackageVersionsCentrally false

using WeihanLi.Web.Extensions;

var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.MapRuntimeInfo();
await app.RunAsync();

同樣地,執行 dotnet run webapi.cs

webapi runtime-info sample

  • 當你的腳本變複雜,需要更多自訂時不想使用檔案程式,只需一條命令即可將其轉換為標準專案,CLI 會自動產生 .csproj 檔案,並移轉相關設定,程式碼行為不會改變:
dotnet project convert webapi.cs

轉換之後的專案如下,也可以直接使用 dotnet run 執行

converted project info

支援 Linux/Unix 下的 shebang(#!),我們可以讓 C# 腳本像 Bash/Python 一樣直接執行,可以執行 ./hello.cs

#!/usr/bin/dotnet run
Console.WriteLine("Hello, .NET!");

shebang setting

未來功能

透過 dotnet run file.cs 直接執行 C# 檔案的提案,為 .NET 帶來了靈活強大的腳本體驗。設計文件中也展望了許多未來可能的增強,使檔案式程式在 .NET 生態中變得更實用,後續還有一些功能特性

1. 目標路徑的擴充支援

  • 資料夾作為目標:允許將資料夾作為 dotnet run 的目標(如:dotnet run ./my-app/),可直接執行該資料夾下的主進入點。
  • 標準輸入與內嵌程式碼:支援 dotnet run --cs-from-stdin 透過標準輸入讀取 C# 程式碼,或用 dotnet run --cs-code 'Console.WriteLine("Hi")' 實現原始程式碼腳本。

2. 統一命令列參數

  • 目錄與檔案選項:引入 --directory--file 或統一的 --path 選項,讓檔案式和專案式程式的命令體驗一致。
  • 多進入點支援:增加如 --entry 等參數,便於在多程式目錄中選擇指定進入點執行。

3. 增強整合與一致性

  • 成長前後的無縫體驗:確保無論是檔案式還是升級為專案式,dotnet run 等命令都能順暢執行。
  • 通用選項:未來可提供一個適用於專案或檔案兩種格式的統一參數,讓格式切換更加平滑。

4. 效能與易用性提升

  • 選擇性包含檔案:透過參數或指令控制包含哪些檔案,減少意外包含大量檔案帶來的困擾和效能負擔。
  • 巢狀檔案錯誤提示:如子目錄包含過多 .cs 檔案或 .csproj 檔案時,可考慮報錯或警告,避免無意間編譯大量內容。

5. 多進入點場景的實作優化

  • 專案結構生成:優化多進入點目錄「成長」為專案時的結構生成,合理劃分專案子目錄,共用程式碼處理更清晰。
  • 進階存取控制:探索 InternalsVisibleTo 等特性用於更好的程式碼共用,並考慮未來 C# 語言的增強。

6. Shebang 與 Shell 支援

  • 專用可執行檔:可能會發佈 dotnet-rundotnet-run-file 可執行檔,提升 shebang(#!)在不同 shell 環境下的相容性,讓腳本跨平台直接執行。
  • 支援 /usr/bin/env:研究如何規避 shell 使用 shebang 時的參數傳遞限制,提高 CLI 腳本的易用性。

7. 更多檔案式程式命令支援

  • 建構與還原:擴充 dotnet restore file.csdotnet build file.cs 等命令,便於 IDE 和 CI 整合檔案式程式。
  • 套件管理dotnet package add 可直接向 C# 檔案頂部新增 #:package 指令,簡化腳本相依性管理。

8. 明確檔案匯入

  • 匯入指令:未來可能支援 #import ./another-file.cs 等指令,明確指定需包含的 C# 檔案,開發者控制更靈活。

其他

目前我們還需要使用 dotnet run hello.cs,後續會可以簡化成 dotnet hello.cs,還可以執行原始程式碼,期待後面更好用的特性了~~

更多資訊可以參考官方文件介紹: https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md

感覺有點不完美的是又建立了一種新的 nuget package 引用語法,和 dotnet-script 裡 nuget: package@version 不同,和 dotnet-script 不太相容

目前還不支援自訂進入點,後面如果能夠支援自訂進入點方法就更好了,期待~~

最後在 build 大會上有一個 dotnet run file 的介紹影片,大家感興趣可以看一下

【影片可點擊原公眾號查看】

參考資料

繼續探索

延伸閱讀

更多文章