概要
.NET 11 は2026年11月ごろの正式リリースが予定されています。プレビューも後半戦に入ってきましたので、これまでの Preview で追加された機能をトピックごとに振り返っておきたいと思います。
本記事では C# 15 で追加された言語機能について、Preview 5 時点の内容をまとめます。C# 15 は .NET 11 プレビューに同梱されており、最新の Visual Studio 2026 Insiders もしくは .NET 11 プレビュー SDK で試すことができます。
Preview 5 の C# リリースノートで挙げられている言語・コンパイラの更新は以下の3つです。
- 閉鎖クラス階層(Closed class hierarchies)
- union 宣言と union パターン
- unsafe コードの進化(Unsafe Evolution)
加えて、C# 15 の新機能として「コレクション式の引数」も利用できるようになっているため、最後に補足として紹介します。
本記事は .NET 11 Preview 5 の C# リリースノートおよび公式ドキュメントを元に整理しています。いずれもプレビュー段階の機能で、正式リリースまでに仕様が変わる可能性がある点にはご注意ください。特に閉鎖クラス階層・union・unsafe の進化については、現時点ではコンパイラのサポート属性をプロジェクト側で定義する必要があるものもあります。
閉鎖クラス階層(Closed class hierarchies)
closed を付けたクラスは、同じアセンブリ内からのみ直接派生できる クラスになります。派生型が限定されることで、コンパイラは switch 式の網羅性チェックに既知の派生型を利用できるようになります。
public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;
static string Describe(GateState state) => state switch
{
Closed => "closed",
Open(var percent) => $"{percent}% open"
};
閉鎖クラスのルールは以下の通りです。
-
closedクラスは暗黙的にabstractになる - 直接の派生型は、閉鎖された基底クラスと同じアセンブリ内で宣言する必要がある
- 到達可能なすべての直接派生型を扱っていれば、
switch式は網羅的とみなされる
派生型が閉じているとコンパイラが「これで全部」と判断できるため、default を書かなくても網羅性が保証されるのが嬉しいポイントです。
なお、このプレビューでは閉鎖クラスを定義するプロジェクトで、以下のコンパイラサポート属性をコンパイル対象に用意しておく必要があります。
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ClosedAttribute : Attribute { }
union 宣言と union パターン
union 宣言では、値が 固定されたケース型のうちいずれか1つ になる値型を作成できます。パターンマッチングで union の値を取り出すことができ、すべてのケース型を扱っていれば switch 式は網羅的になります。
public record class Dog(string Name);
public record class Cat(int Lives);
public union Pet(Dog, Cat);
static string Describe(Pet pet) => pet switch
{
Dog(var name) => $"dog: {name}",
Cat(var lives) => $"cat: {lives}"
};
このプレビューでの union のルールは以下の通りです。
- コンパイラが各ケース型に対して public なコンストラクターを生成する
- ケースの値は union 型へ暗黙的に変換される
- パターンマッチングは、多くのパターンを union が内包する値に対して適用する
- あいまいな union 変換はエラーになる
また、System.Runtime.CompilerServices.UnionAttribute を付けて手書きした union 型(ネストした IUnionMembers プロバイダーインターフェイスによる union メンバーの指定を含む)もコンパイラが認識します。
F# の判別共用体や TypeScript のユニオン型に慣れている方には馴染みやすい機能だと思います。「取りうる型が決まっている値」を安全に表現できるので、状態やイベントのモデリングでの活躍が期待できそうです。
union もこのプレビューでは、宣言するプロジェクトでコンパイラサポート型をコンパイル対象に用意しておく必要があります。
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public sealed class UnionAttribute : Attribute { }
public interface IUnion
{
object? Value { get; }
}
unsafe コードの進化(Unsafe Evolution)
こちらは .NET 11 のプレビュー機能です。
「Unsafe Evolution」は、[RequiresUnsafe] のような別の属性に頼るのではなく、unsafe そのものが 「unsafe を必要とするメンバー」 を直接表せるようにしていく取り組みです。まだプレビュー機能で、正式リリースまでに仕様やルールが変わる可能性があります。
Preview 5 では、ポインター型やポインター値を unsafe コンテキストの外側にも記述できる ようになりました。unsafe の境界は、アンマネージドメモリを参照(デリファレンス)する操作や、unsafe とマークされたメンバーを呼び出す操作の側へと移動します。
int value = 42;
int* pointer = &value;
unsafe
{
System.Console.WriteLine(*pointer);
}
上記では、ポインターの宣言(int* pointer)とアドレス取得(&value)が unsafe ブロックの外側で許可されています。一方で、ポインターのデリファレンス(*pointer)は引き続き unsafe コンテキストを必要とします。「本当に危険な操作」だけに unsafe を絞り込めるようになる方向性です。
あわせて、スタック割り当てのルールもオプトインになりました。[SkipLocalsInit] メンバー内で stackalloc を Span<T> / ReadOnlySpan<T> に変換する場合、更新後のメモリ安全性ルールをプロジェクトが選択したときにのみ unsafe コンテキストが必要になります。
まとめ
- Preview 5 の C# リリースノートでは「閉鎖クラス階層」「union 宣言と union パターン」「unsafe コードの進化」の3つが挙げられている
- 閉鎖クラス階層と union は、いずれも
switch式の網羅性チェックと組み合わせて「取りうる型が決まっている値」を安全に扱えるようにする機能 - unsafe コードの進化では、ポインター型・ポインター値を
unsafeの外側に書けるようになり、unsafeの境界が「本当に危険な操作」側へ移動した
いずれもプレビュー段階の機能で、コンパイラサポート属性をプロジェクト側で用意する必要があるものもあります。今後の変更もありえますが、正式リリースに向けて触っておくと変化を追いやすくなると思います。
この記事が皆様のコーディングライフの助けになれば幸いです。
参考