.NET 10で、dotnet app.csのように、C#ファイルを直接実行できる機能が追加されました🎉 公式での呼称は「File-based apps」です。
機能面の概要は
を見てもらえば良いので、ここでは実際に使ってみて気づいた点をまとめておきます。
なお、.NET 10.0.2で確認しています。
概要
おさらいです。
dotnet run app.cs あるいは dotnet app.cs とすることで、C#ファイルを直接実行できます。
#!/usr/bin/env dotnet
// echo dotnet version
Console.WriteLine($"dotnet version: {Environment.Version}");

パッケージを使いたい場合は#:package (パッケージ名)@(バージョン)の記法を使えます。
#:package Humanizer@2.14.1
using Humanizer;
var dotNet9Released = DateTimeOffset.Parse("2024-12-03");
var since = DateTimeOffset.Now - dotNet9Released;
Console.WriteLine($"It has been {since.Humanize()} since .NET 9 was released.");

なお、どうせ使い捨ての実行であることが多いと思うので、パッケージは@*で最新版を使うのが便利です。
#:package Humanizer@*
指定無しで最新版使ってくれよというIssueも立ってます。.NET11で入るかも?
Preview 6以降ではプロジェクト参照が使えるようになっています。作ったライブラリの動作確認に使えるのでとてもありがたい!
ディレクトリ指定でも利用可能なのも嬉しいですね。
#:project ../MyLibrary/MyLibrary.csproj
// または
#:project ../MyLibrary
罠ポイント
標準でNativeAOT
リフレクションをうっかり使うと死にます。
そんなの使わないよと思うかもしれませんが、例えばSystem.Text.Json等のシリアライズは既定ではリフレクションを使うので普通にコケます。
もちろん明示的にOffにできますが、Onで動くコードを書くように努めたほうが良さそうです。
// Offにするとき
#:property PublishAot=false
ビルドキャッシュに注意
特にプロジェクト参照をしているときに注意が必要だと思います。標準では以下の内容で出力をキャッシュしています。
- Source file content.
- 要するにファイルの中身。それはそう。
- Directive configuration.
#:packageや#:projectなどの構成内容
- SDK version.
- ビルドに使ったSDKのバージョン。これも当然。
- Implicit build file existence and content.
- これは
Directory.Build.propsやnuget.configなどの話。
- これは
参照プロジェクトだけをビルドし直した場合、変更が反映されずにあれ?となります。AIもこれに戸惑っていたので注意したほうが良さそうです。
なお、以下のコマンドで明示的にビルドし直したうえで実行することができます。
dotnet run app.cs --no-cache
Analyzerを参照できない
アナライザーやSourceGeneratorはそのままだと(現時点では)参照できません。
ExcludeAssets等を指定することができないという文法上の話ですね。
回避策としては、Directory.Build.propsは使えるのでそこにProjectReferenceを追加します。
(あるいは、普通にprojectに移行する)
対応する予定はあるっぽいので期待。実施される場合は#:package (package)@(version) ExcludeAssets=runtime PrivateAssets=allのような構文になりそう。
最新機能のウォッチ
色々な人が記事として取り上げてくれているのはいいんですが、大体が.NET 10 Preview 4の時点で止まっているのが悲しみ……。公式は公式で微妙に内容がないし。
というわけで、公式Issuesのウォッチをしたほうが良さそうです。
編集履歴
- 2026-02-17: 公開
- 2026-02-19:
dotnet run app.cs --no-cacheの情報を追加