はじめに
.NET 11のリリース予定日である2026年11月がだいぶ近づいてきました。プレビュー版もPreview 7まで進み、正式版でどんな機能が入ってくるかがかなり見えてきた状態です。
せっかくなので、現時点のプレビューで公開されている情報を一通り整理して、実際に役立ちそうな機能を中心にまとめてみます。まだプレビュー段階なので正式リリースまでに変更・削除される可能性がある点はご了承ください。
2026年8月時点(.NET 11 Preview 7)の情報を元にしています。正式版では仕様が変わる可能性があります。
.NET 11の位置づけ
.NETは1年ごとにメジャーバージョンがリリースされ、偶数バージョンがLTS(長期サポート、3年間)、奇数バージョンがSTS(標準サポート、1年半)というサイクルになっています。
| バージョン | 種別 | リリース |
|---|---|---|
| .NET 8 | LTS | 2023年11月 |
| .NET 9 | STS | 2024年11月 |
| .NET 10 | LTS | 2025年11月 |
| .NET 11 | STS | 2026年11月(予定) |
プリザンターはLTSのタイミングでターゲットフレームワークを更新する運用なので(プリザンターを.NET 8から.NET 10に移行してみるを参照)、.NET 11自体がすぐに採用されるわけではありません。とはいえSTSでも新しい言語機能やランタイム改善は本流の.NETに取り込まれていくので、次のLTSである.NET 12に向けた予習として押さえておく価値があります。
C# 15の新機能を試してみる
.NET 11にはC# 15が同梱されます。中でも実務に効いてきそうな機能をいくつか見てみます。
Union types(合併型)
複数の型のうちどれか1つを表す型を、新しいunionキーワードで宣言できるようになりました。従来は継承やラッパークラスで表現していた「AかBのどちらか」を、言語レベルでサポートします。
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public union Pet(Cat, Dog, Bird);
各ケース型からの暗黙変換が提供され、switch式で全ケースを網羅しているかをコンパイラがチェックしてくれます。
Pet pet = new Dog("Rex");
string name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
API のレスポンスとして「成功時の型」と「エラー時の型」のどちらかを返したいケースなど、いままでOneOfのような外部ライブラリに頼っていた場面で標準機能として使えるようになりそうです。ASP.NET Core側でもSystem.Text.Jsonを使う箇所(Minimal APIのリクエスト/レスポンス、SignalRのJsonHubProtocol、Blazorのコンポーネントパラメータなど)で合併型がサポートされます。
public union UnionIntString(int, string);
app.MapGet("/value", () => new UnionIntString(42));
仕様書に記載されている機能の一部はまだ未実装で、今後のプレビューで追加される予定とのことです。
Extension indexers(拡張インデクサー)
extensionブロックの中にインデクサーを宣言できるようになりました。既存の型にインデックスアクセスを後付けできます。
public static class SequenceIndexer
{
extension(IEnumerable<int> sequence)
{
public int this[int index] => sequence.ElementAt(index);
}
}
IEnumerable<int> numbers = Enumerable.Range(1, 10);
int third = numbers[2];
IEnumerable<T>のような遅延評価のシーケンスに対して、まるでリストのように[]でアクセスできるようになるので、ちょっとしたユーティリティ拡張の書き味が良くなりそうです。
ラベル付きbreak/continue
ネストしたループの内側から外側のループを抜けたい・次のイテレーションに進みたい、というときに使うラベル付きのbreak / continueが追加されました。
outer: for (int row = 0; row < grid.Height; row++)
{
for (int column = 0; column < grid.Width; column++)
{
if (grid[row, column].IsBlocked)
{
continue outer;
}
if (grid[row, column].IsGoal)
{
break outer;
}
}
}
これまではフラグ変数を用意して外側のループでチェックする、gotoでループの外にジャンプする、といった書き方が必要でしたが、ラベルを直接指定するだけで済むようになります。IDE0410というスタイルルールが、こうしたフラグ変数やgotoのパターンを検出してラベル付きジャンプへの置き換えを提案してくれます。
Collection expression arguments
コレクション式[...]の先頭にwith(...)要素を書くことで、コンストラクタや生成メソッドに引数を渡せるようになりました。
string[] values = ["one", "two", "three"];
// List<T>のコンストラクタにcapacityを渡す
List<string> names = [with(capacity: values.Length * 2), .. values];
// HashSet<T>のコンストラクタにcomparerを渡す
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// OrdinalIgnoreCaseで比較されるため要素は1つだけになる
初期サイズを指定してリストの再確保を減らしたり、大文字小文字を無視したHashSet<T>を作ったりが、コレクション式の記法のまま書けるようになるのは地味に嬉しい変更です。
ランタイムの改善:Runtime Async
.NET 11ランタイムの目玉の1つが「Runtime Async」です。従来async/awaitはC#コンパイラがステートマシンを生成していましたが、Runtime Asyncではランタイム自体が非同期処理をネイティブにサポートするようになります。
主なメリットとして次のような点が挙げられています。
- スタックトレースがより素直になり、デバッグ時に追いやすくなる
- 継続処理のオーバーヘッドが減り、パフォーマンスが向上する
- 周辺状態が無い場合は
ExecutionContextのキャプチャを省略できる
net11.0をターゲットにするプロジェクトでは<EnablePreviewFeatures>true</EnablePreviewFeatures>なしでRuntime Asyncが使えるようになっており、ASP.NET Coreの共有フレームワークライブラリ自体もRuntime Asyncでビルドされるようになりました。async/awaitを書き換える必要はなく、既存コードのままメリットを受けられる改善です。
async/awaitのコード生成方法が変わる大きな変更なので、例外のスタックトレースやExecutionContext/AsyncLocalの伝播まわりで.NET 10までと挙動が変わる可能性があります。プレビューで一度動作確認しておくと安心です。
ライブラリの新機能
LINQ の FullJoin
Enumerable / Queryable / AsyncEnumerableに、FullJoinとタプルを返すJoin / GroupJoinのオーバーロードが追加されました。
var employees = new[]
{
new { Id = 1, Name = "山田" },
new { Id = 2, Name = "佐藤" },
};
var departments = new[]
{
new { EmployeeId = 1, Department = "営業" },
new { EmployeeId = 3, Department = "総務" },
};
var result = employees.FullJoin(
departments,
e => e.Id,
d => d.EmployeeId,
(e, d) => new
{
Name = e?.Name ?? "(所属なし社員)",
Department = d?.Department ?? "未所属",
});
これまでFullJoinは左外部結合と右外部結合を組み合わせて自前実装するのが定番でしたが、標準のLINQメソッドとして使えるようになります。片方にしか存在しないデータの突き合わせを書くときに便利です。
Process の使い勝手向上
System.Diagnostics.Processにプロセス起動・取得まわりのヘルパーが追加されました。
// 例外を投げずに安全にプロセスを取得する
if (Process.TryGetProcessById(processId, out var process))
{
Console.WriteLine(process.ProcessName);
}
StartSuspendedでサスペンド状態のまま起動できるようになるなど、外部プロセスをより細かく制御したい場面での選択肢が広がっています。
Zstandard圧縮のサポート
System.IO.Compression名前空間でZstandard(zstd)圧縮がサポートされました。ASP.NET Core側でも応答の圧縮・リクエストの解凍でzstdが既定で有効になります。
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
options.CompressionOptions = new ZstandardCompressionOptions
{
Quality = 6, // 1〜22。数値が大きいほど圧縮率は上がるが遅くなる
};
});
gzipやBrotliに比べて圧縮率と速度のバランスが良いとされるzstdが標準ライブラリだけで使えるようになるのは、通信量を減らしたいAPIサーバにとって嬉しい変更です。
ASP.NET Core 11で気になった機能
Minimal APIの非同期バリデーション
これまでValidationAttributeは同期処理前提でしたが、AsyncValidationAttributeとIAsyncValidatableObjectが追加され、DBやリモートAPIへの問い合わせを伴うバリデーションを非同期に書けるようになりました。
public sealed class UniqueEmailAttribute : AsyncValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext context) =>
throw new InvalidOperationException("このアトリビュートはIsValidAsyncで検証してください。");
protected override async Task<ValidationResult?> IsValidAsync(
object? value, ValidationContext context, CancellationToken cancellationToken)
{
var users = context.GetRequiredService<IUserService>();
if (value is string email && await users.EmailExistsAsync(email, cancellationToken))
{
return new ValidationResult("そのメールアドレスは既に登録されています。");
}
return ValidationResult.Success;
}
}
builder.Services.AddValidation();
app.MapPost("/users", (CreateUserRequest request) => Results.Ok(request));
メールアドレスの重複チェックやマスタデータとの整合性チェックなど、DBアクセスを伴うバリデーションを標準の仕組みに乗せられるのは実務でかなり使う場面が多そうです。
[ShortCircuit]属性でミドルウェアをスキップ
ヘルスチェックやrobots.txtのように認証・CORSなどのミドルウェアを通す必要が無いエンドポイントに[ShortCircuit]を付けると、ルーティング直後にレスポンスを返せるようになります。
app.MapGet("/health", [ShortCircuit] () => "Healthy");
MVCのコントローラ・アクションにもそのまま適用できます。
[ApiController]
[Route("robots.txt")]
[ShortCircuit]
public class RobotsController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Content("User-agent: *\nDisallow:", "text/plain");
}
不要なミドルウェアの処理コストを避けられるので、高頻度で叩かれるヘルスチェック系エンドポイントとの相性が良さそうです。
OpenTelemetryトレースがネイティブ対応
これまでOpenTelemetry.Instrumentation.AspNetCoreパッケージが担っていたHTTPサーバのトレース属性付与が、ASP.NET Core本体に組み込まれました。
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore")
.AddConsoleExporter());
追加のインストルメンテーションライブラリなしでhttp.request.methodやurl.pathなどのOpenTelemetryセマンティック規約に沿った属性が付与されるようになるため、依存パッケージを1つ減らせます。
SDK・CLI周りの改善
dotnet run -e で環境変数を渡す
dotnet runにコマンドラインから環境変数を渡せる-eオプションが追加されました。
dotnet run -e ASPNETCORE_ENVIRONMENT=Staging -e API_KEY=dummy-key
ローカルでちょっと環境を変えて動かしたいときに、いちいちlaunchSettings.jsonを編集しなくて済むのは地味に便利です。
ファイルベースアプリの#:include
単一の.csファイルだけで実行できる「ファイルベースアプリ」に、複数ファイルへの分割をサポートする#:includeディレクティブが追加されました。
#:include ./libs/Helpers.cs
#:include ./libs/MyLib.dll
Console.WriteLine(Helpers.Greet("world"));
ビルド済みのDLLを直接参照することもできるので、ちょっとしたスクリプトが育ってきて複数ファイルに分けたくなった、というときにプロジェクトファイルを作らずに対応できます。
dotnet testの機能強化
dotnet testに--no-dependencies、--use-current-runtime、--timeout、--maximum-failed-testsといったオプションが追加され、CI向けの実行制御がしやすくなりました。テスト実行中の様子もライブ表示されるようになっています。
dotnet test --timeout 5m --maximum-failed-tests 10
失敗が多いテストスイートを早期に打ち切ったり、CI全体のタイムアウトより先にテストランナー側でタイムアウトさせたりといった調整がしやすくなります。
まとめ
.NET 11はSTSリリースということもあり、プリザンター本体がすぐに追従するバージョンではありませんが、C# 15の合併型やラベル付きジャンプ文、Runtime Asyncのようにコードの書き方や実行時の挙動に関わる変更が多く、次のLTSである.NET 12を見据えるうえでも押さえておいて損はなさそうです。
正式リリースは2026年11月の予定なので、気になった機能があれば.NET 11プレビューSDKを入れて実際に触ってみることをオススメします。仕様がまだ固まっていない機能もあるので、フィードバックがあれば各リポジトリのIssueに投げてみるのも良さそうです。