csharp-scripts について
.NET 10以降でファイルベースの小規模なC#アプリを作成・実行する開発者向けの手順です。単一・複数ファイルの構成、ディレクティブ、旧SDK向けの代替手順を扱います。
主な機能
- 01トップレベルのエントリーファイル作成
- 02ディレクティブによるNuGetパッケージとプロパティの追加
- 03補助ファイルの取り込みやプロジェクト参照
- 04dotnetでの実行とクリーンアップ
- 05SDKバージョンの確認と旧SDKへの対応
活用シーン
- 01C#の言語機能やAPIの簡単な検証
- 02大規模プロジェクトへの統合前のロジック検証
- 03少数の.csファイルで小規模なツールを作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill csharp-scriptsインストール後、Claude Code で/csharp-scriptsを入力して読み込まれたか確認します。
詳細
ファイルベースの C# アプリ
使用する場面
- C# の概念、API、言語機能をファイルベースのアプリですばやくテストする場合
- 大規模なプロジェクトに統合する前にロジックを試作する場合
- エントリーポイントのファイル 1 つと、いくつかの補助
.csファイルで小規模なユーティリティを作成する場合
使用しない場面
- 言語を問わない簡易スクリプト、一時的な計算、またはシェル/Python/PowerShell のような自動化をユーザーが求めている場合
- 既存のアプリで、完全なプロジェクト、ソリューションへの統合、またはプロジェクト参照が必要な場合
- ユーザーが既存の .NET ソリューション内で作業しており、そこにコードを追加したい場合
- アプリの規模が大きく、プロジェクト構成、ビルドのカスタマイズ、テスト、または発行の設定を
.csprojに置くべき場合
入力
| 入力 | 必須 | 説明 |
|---|---|---|
| C# コードまたは意図 | はい | 実行するコード、またはファイルベースのアプリに実行させる処理の説明 |
ワークフロー
ステップ 1:.NET SDK のバージョンを確認する
dotnet --version を実行して SDK がインストールされていることを確認し、フィーチャーバンドを含む完全なバージョンを記録します。ファイルベースのアプリには .NET 10 以降が必要です。#:include、#:exclude、および推移的なディレクティブ処理には SDK 10.0.300 以降が必要です。SDK 10.0.100/10.0.200 のビルドでは単一ファイルのアプリを実行できますが、これらの複数ファイル用ディレクティブはサポートされていません。バージョンが 10 未満の場合は、代わりに旧バージョンの SDK 向けのフォールバックに従ってください。
ステップ 2:アプリのファイルを作成する
トップレベルステートメントを使用して、エントリーポイントとなる .cs ファイルを作成します。.csproj ファイルとの競合を避けるため、既存のプロジェクトディレクトリの外に配置してください。
#!/usr/bin/env dotnet
// hello.cs
Console.WriteLine("Hello from a file-based app!");
var numbers = new[] { 1, 2, 3, 4, 5 };
Console.WriteLine($"Sum: {numbers.Sum()}");
ガイドライン:
- トップレベルステートメントを使用します(
Mainメソッド、クラス、名前空間の定型コードは不要です) usingディレクティブはファイルの先頭に配置します(存在する場合は、#!行および#:ディレクティブの後)- 型宣言(クラス、レコード、列挙型)は、すべてのトップレベルステートメントの後に配置します
ステップ 3:アプリを実行する
dotnet hello.cs
ファイルを自動的にビルドして実行します。結果はキャッシュされるため、2 回目以降の実行は高速です。引数は -- の後に渡します。
dotnet hello.cs -- arg1 arg2 "multi word arg"
ステップ 4:必要に応じてディレクティブを追加する
ディレクティブはファイルの先頭(省略可能なシバン行がある場合はその直後)に配置し、using ディレクティブやその他の C# コードより前に記述します。すべてのディレクティブは #: で始まります。
#:package — NuGet パッケージ参照
アプリで意図的に一元管理されたパッケージ管理を使用する場合を除き、バージョンを指定してください。利用可能な最新パッケージで問題ない場合は @* を使用します(プレリリース版の場合は @*-*)。
#:package [email protected]
using Humanizer;
Console.WriteLine("hello world".Titleize());
#:property — MSBuild プロパティ
任意の MSBuild プロパティをインラインで設定します。構文:#:property PropertyName=Value
#:property AllowUnsafeBlocks=true
#:property PublishAot=false
#:property NoWarn=CS0162
MSBuild 式およびプロパティ関数がサポートされています。
#:property LogLevel=$([MSBuild]::ValueOrDefault('$(LOG_LEVEL)', 'Information'))
一般的なプロパティ:
| プロパティ | 目的 |
|---|---|
AllowUnsafeBlocks=true | unsafe コードを有効にする |
PublishAot=false | ネイティブ AOT を無効にする(既定では有効) |
NoWarn=CS0162;CS0219 | 特定の警告を抑制する |
LangVersion=preview | プレビュー版の言語機能を有効にする |
InvariantGlobalization=false | 文化固有のグローバリゼーションを有効にする |
#:project — プロジェクト参照
相対パスで別のプロジェクトを参照します。
#:project ../MyLibrary/MyLibrary.csproj
#:ref — ファイルベースアプリの参照
別の .cs ファイルを、同じコンパイルに含めるのではなく、別個のアセンブリにコンパイルする必要がある場合は、ファイルベースアプリの別プロジェクトとして参照します。同じアセンブリをエントリポイントと共有する通常のヘルパーファイルには #:include を使用します。プロジェクト参照のような境界を設ける場合は #:ref を使用します。
#:property ExperimentalFileBasedProgramEnableRefDirective=true
#:ref ../Shared/Formatter.cs
Console.WriteLine(Formatter.Title("hello world"));
ガイドライン:
- 参照先のファイルは独自の仮想プロジェクトとしてコンパイルされ、プロジェクト参照として追加されます。
- 参照先のファイルがトップレベルステートメントを含まないライブラリの場合は、そのファイルに
#:property OutputType=Libraryを記述します。 - 参照元のアプリで使用するメンバーは public にする必要があります。internal メンバーはアセンブリ境界を越えて参照できません。
#:refは推移的です。参照先のファイルに独自の#:refや他の#:ディレクティブを含めることができます。- 相対パスはディレクティブを含むファイルを基準に解決されます。
- 一部の SDK ビルドでは
#:property ExperimentalFileBasedProgramEnableRefDirective=trueが必要です。SDK がこれなしで#:refを受け付ける場合は、そのプロパティを削除してください。
#:sdk — SDK の選択
既定の SDK(Microsoft.NET.Sdk)を上書きします。
#:sdk Microsoft.NET.Sdk.Web
#:include と #:exclude — マルチファイルアプリ
.NET SDK 10.0.300 以降では、ファイルベースアプリに追加ファイルを同じ仮想プロジェクト内に含めることができます。これらのディレクティブを使用する前に、dotnet --version の出力全体を確認してください。10.0.100 または 10.0.200 の SDK も .NET 10 ですが、これらのディレクティブはサポートされていません。ヘルパーソースファイルやサポートされているアセットには #:include を、インクルードパターンまたは既定の項目セットからファイルを除外するには #:exclude を使用します。
#!/usr/bin/env dotnet
#:include Helpers.cs
#:include Models/*.cs
#:exclude Models/Generated/*.cs
Console.WriteLine(Formatter.Title("hello world"));
ガイドライン:
dotnetに渡すファイルをエントリポイントとして扱い、トップレベルステートメントをそこに記述します。- クラス、レコード、列挙型などの宣言は、インクルードする
.csファイルに記述します。 - 広範な再帰グロブより、
Helpers.csやModels/*.csなどの明示的なグロブを優先します。 - パスはディレクティブを含むファイルを基準に解決されます。
- エントリポイント以外の C# ファイルにあるインクルードディレクティブも処理されるため、ヘルパーファイルで独自の
#:package、#:property、#:sdk、#:project、#:ref、#:include、または#:excludeディレクティブを宣言できます。 - ディレクティブの種類が重複を明示的にサポートしている場合を除き、インクルードするファイル間でディレクティブを重複させないでください。
#:package、#:property、#:sdk、#:include、および#:excludeの重複項目はエラーになる場合があります。 - アプリで
#:includeを使用する場合、ツールにエントリポイントを明確に示すため、Unix 系システムではエントリポイントファイルにシェバン(#!/usr/bin/env dotnet)を追加します。シェバンを含むファイルにはLFの改行コードを使用し、BOM は付けないでください。
レイアウト例:
scratch/
hello.cs
Helpers.cs
Models/
Person.cs
#!/usr/bin/env dotnet
// hello.cs
#:include Helpers.cs
#:include Models/*.cs
var person = new Person("Ada");
Console.WriteLine(Formatter.Title(person.Name));
// Helpers.cs
static class Formatter
{
public static string Title(string value) => value.ToUpperInvariant();
}
// Models/Person.cs
record Person(string Name);
ステップ 5:クリーンアップ
ユーザーが使い終わったら、アプリのファイルを削除します。ビルドのキャッシュ成果物を消去するには:
dotnet clean hello.cs
Unix のシェバンのサポート
Unix プラットフォームでは、.cs ファイルを直接実行可能にできます。
-
ファイルの先頭行にシェバンを追加します。
#!/usr/bin/env dotnet Console.WriteLine("I'm executable!"); -
実行権限を設定します。
chmod +x hello.cs -
直接実行します。
./hello.cs
シェバンを追加する場合は、改行コードに LF(CRLF ではなく)を使用してください。このディレクティブは Windows では無視されます。
ソース生成 JSON
ファイルベースアプリでは、既定でネイティブ AOT が有効になります。JsonSerializer.Serialize<T>(value) のようなリフレクションベースの API は、AOT では実行時に失敗します。代わりにソース生成シリアル化を使用してください。
using System.Text.Json;
using System.Text.Json.Serialization;
var person = new Person("Alice", 30);
var json = JsonSerializer.Serialize(person, AppJsonContext.Default.Person);
Console.WriteLine(json);
var deserialized = JsonSerializer.Deserialize(json, AppJsonContext.Default.Person);
Console.WriteLine($"Name: {deserialized!.Name}, Age: {deserialized.Age}");
record Person(string Name, int Age);
[JsonSerializable(typeof(Person))]
partial class AppJsonContext : JsonSerializerContext;
プロジェクトへの変換
ファイルベースアプリがこのワークフローでは対応しきれなくなったら、完全なプロジェクトに変換します。
dotnet project convert hello.cs
.NET 9 以前の代替方法
.NET SDK のバージョンが 10 未満の場合、ファイルベースアプリは利用できません。代わりに一時的なコンソールプロジェクトを使用します。
mkdir -p /tmp/csharp-file-based-app && cd /tmp/csharp-file-based-app
dotnet new console -o . --force
生成された Program.cs をアプリの内容に置き換え、dotnet run で実行します。NuGet パッケージは dotnet add package <name> で追加します。完了したらディレクトリを削除します。
検証
-
dotnet --versionで 10.0 以降が報告される(またはフォールバックパスが使用される) - アプリで
#:include、#:exclude、またはインクルードされたファイルからの推移的なディレクティブを使用している場合、dotnet --versionで SDK 10.0.300 以降が報告される - アプリがエラーなしでコンパイルされる(
dotnet build <file>.csで明示的に確認できます) -
dotnet <file>.csを実行すると期待どおりの出力が得られる - 複数ファイルのアプリに必要なすべてのヘルパーファイルが含まれ、意図しないファイルが除外されている
- セッション終了後にアプリのファイルとキャッシュされた成果物がクリーンアップされている
よくある落とし穴
| 落とし穴 | 解決方法 |
|---|---|
.cs ファイルが .csproj のあるディレクトリ内にある | アプリをプロジェクトディレクトリの外に移動するか、dotnet run --file file.cs を使用します |
バージョンを指定せずに #:package を使用している | バージョンを指定します:#:package [email protected]、または最新バージョンには @* を指定します |
#:property の構文が正しくない | = の前後にスペースや引用符を入れず、PropertyName=Value の形式を使用します:#:property AllowUnsafeBlocks=true |
| ディレクティブが C# コードの後に配置されている | すべての #: ディレクティブは、(存在する場合は)省略可能なシバン行の直後、かつ using ディレクティブやその他の C# ステートメントより前に配置する必要があります |
| ヘルパーファイルがコンパイルされない | エントリーポイントファイルに #:include Helper.cs または適切な glob を追加します |
| 共有ファイルでアセンブリ境界が必要 | #:include Shared.cs の代わりに #:ref Shared.cs を使用し、参照先ファイルにエントリーポイントがない場合は、そのファイルで #:property OutputType=Library を設定します |
| 広範な include に無関係なファイルが含まれる | 対象を絞った include パターンを優先し、生成ファイル、バックアップファイル、実験用ファイルには #:exclude を使用します |
| インクルードされたファイル内に重複したディレクティブがある | パッケージ、プロパティ、SDK、include、exclude の各ディレクティブが、エントリーポイントとインクルードされた C# ファイル全体で重複しないようにします |
| リフレクションベースの JSON シリアル化が失敗する | ソース生成 JSON と JsonSerializerContext を使用します(ソース生成 JSON を参照) |
| ビルドの動作が予期しない、またはバージョンエラーが発生する | ファイルベースのアプリは、親ディレクトリから global.json、Directory.Build.props、Directory.Build.targets、nuget.config を継承します。継承された設定が競合する場合は、アプリを独立したディレクトリに移動します |
詳細情報
ファイルベースのアプリに関する完全なリファレンスについては、https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps を参照してください。