はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】.NET10 Preview4からfile-based appsというcsprojなしで.csファイルを直接実行できる機能が追加された

はじめに

.NET10 Preview4からfile-based appsという.csprojなしで.csを実行できる機能が追加されました。

devblogs.microsoft.com

手軽に利用できるようになったのは勿論のこと、特にCI/CDで.csを利用する手法がより広がっていくのではと思っています。前にProcessX(Zx) + ConsoleAppFrameworkの組み合わせならCI/CDでbash/zsh/pwshの代わりに使っても良いのではと書いたことがあるのですが、より現実味を帯びてきたと思います。

あとActionsのカスタムシェルを使ってshell: dotnet run {0}とかできたらいいなと思うのですが、調べた感じランナーにインストールされている.NETのバージョンのこともありますし少なくとも現段階では無理そうな気がしています。(追記にコメントしました)
GitHub Actions のワークフロー構文 - GitHub Docs


背景を説明すると、一応.csxとかもあったのですが、基本的に.NETでは.csprojがないと実行をすることができませんでした。

$ tree
.
└── ConsoleApp
    ├── ConsoleApp.csproj
    └── Program.cs

しかし今回のfile-based appsと呼ばれる機能を用いれば、.csprojがいらなくなります。つまり、Program.csのような単一ファイルだけで実行までできるというわけです。

$ chmod +x ./Program.cs
$ ./Program.cs

導入〜使い方までを一通り紹介したいと思います。

導入方法

インストール

.NET10 Preview4をインストールしてください。

dotnet.microsoft.com

VSCodeで開発環境を整える

Riderだとどうなのか確認できていないのですが、VS Codeであれば既にfile-based appsに対応しています。VS CodeにC# Dev Kitという拡張機能があるのでpre-release(私はv1.20.33)をインストールします。

またC#という拡張機能でも、pre-releasev 2.79.8以上にしてください。

VS Code拡張

基本的な使い方

まずは実際に利用する方法を紹介します。

任意の場所に以下の内容の.csを作成してください。

#!/usr/bin/env dotnet run

Console.WriteLine("Hello World!");

Shebangがあるので、実行権限を与えて実行すればOKです。

$ chmod +x ./sample.cs
$ ./sample.cs
Hello World!

使い方

NuGet Packageを参照する

#:packageの後ろに、NuGetのパッケージ名とバージョンを記述します。

#!/usr/bin/env dotnet run

#:package MessagePack@3.1.3

using MessagePack;

パッケージ名が分からない方は、nuget.orgを確認すると良いでしょう。
www.nuget.org

デフォルトのSDKと変更方法

デフォルトのSDKはMicrosoft.NET.Sdkです。WebAPIを作成したいなどでMicrosoft.NET.Sdk.Webといった別のSDKを利用したい場合は#:sdkを記述します。
learn.microsoft.com

#!/usr/bin/env dotnet run

#:sdk Microsoft.NET.Sdk.Web

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run("http://localhost:8080");

MSBuildのpropertyを設定する

MSBuildのPropertyを記述するためには、#:propertyを用います。

<!-- csprojでの記述例 -->
<PropertyGroup>
    <LangVersion>preview</LangVersion>
</PropertyGroup>
#!/usr/bin/env dotnet run

#:property LangVersion preview

Shebang

先ほどからしれっとずっと使っていましたが、ちゃんとShebangも用意してくれています。

#!/usr/bin/env dotnet run
$ chmod +x ./sample.cs
$ ./sample.cs

file-based appをやめる

複雑になってきたら、.csprojを活用した通常のproject-based appsに変換することも可能です。コマンドはdotnet project convert .csへのパスです。

$ tree
.
└── sample.cs

# migrate
$ dotnet project convert sample.cs

# .csprojが作成されていることが確認できる
$ tree
.
└── sample
    ├── sample.cs
    └── sample.csproj

ProcessX + ConsoleAppFrameworkの組み合わせ

ConsoleAppFramework v5ProcessX(Zx)を組み合わせたオススメ構成を紹介して終わりたいと思います。
GitHub - Cysharp/ProcessX: Simplify call an external process with the async streams in C# 8.0.
GitHub - Cysharp/ConsoleAppFramework: Zero Dependency, Zero Overhead, Zero Reflection, Zero Allocation, AOT Safe CLI Framework powered by C# Source Generator.

#!/usr/bin/env dotnet run

#:package ProcessX@1.5.6
#:package ConsoleAppFramework@5.4.1

using ConsoleAppFramework;
using Zx;
using static Zx.Env;

verbose = false;

var app = ConsoleApp.Create();

// root command
app.Add("", async () =>
{
    var tools = await "dotnet tool list -g";
    log(string.Join(",", tools));
});

// 例: ./sample.cs echo --msg "Hello, World!"
app.Add("echo", (string msg) => log(msg));

// 例: ./sample.cs sum 5 10
app.Add("sum", ([Argument] int x, [Argument] int y) => log(x + y));

app.Run(args);
$ ./sample.cs
Package Id                       Version      Commands                   
-------------------------------------------------------------------------
coverlet.console                 6.0.4        coverlet  

$ ./sample.cs sum 1 2
3

$ ./sample.cs echo --msg "hello, world!!!"
hello, world!!!

追記

色々調べてみて、GitHub Actionsでshell: dotnet run {0}を利用できるようにするPRをrunnerに出してみてます!
github.com