C# 12 新機能メモ
.NET 8 (C# 12) が現地時間 2023/11/14 に正式リリースされました。
C# 12 の新機能について、気になったものを備忘録として簡単に纏めます。
プライマリコンストラクタ
通常のクラスおよび構造体に対して、プライマリコンストラクタが追加されました。
これにより、コンストラクタを以下の様に記述できるようになりました。
// 通常のコンストラクタ(C# 11 以前の書き方)
internal class Person1 {
public string Name { get; }
public int Age { get; }
public Person1(string name, int age) {
this.Name = name;
this.Age = age;
}
}
// プライマリコンストラクタ(C# 12 以降の書き方)
internal class Person2(string name, int age) {
public string Name { get; } = name;
public int Age { get; } = age;
}
上記 2 つは機能的に同等のコードであり、.NET 8 ではプライマリコンストラクタを優先して使用するような規則 (IDE0290) が既定で有効になっています。
ただし、先に実装されていた record
型のプライマリコンストラクタとは異なり、プロパティの自動生成は行わないようです。
// record 型はプロパティ Name, Age が自動的に生成される。
internal record Person3(string Name, int Age);
// 通常のクラス・構造体ではプロパティは自動的に生成されない(明示的な宣言が必要)。
internal class Person4(string name, int age);
少し残念な気もしましたが、値オブジェクト以外では不要な場面もあり、値オブジェクトなら record
型を使用すればよい、ということなのでしょう。
コレクション式
配列やリストなどのコレクションを作成するための新しい構文、コレクション式が追加されました。
これにより、各コレクションを以下の様に 共通した構文で 簡潔に記述できるようになりました。
using System.Collections.Immutable;
// 通常のコレクション作成(C# 11 以前の書き方)
int[] a1 = new[] { 1, 2, 3 };
List<int> b1 = new() { 1, 2, 3 };
ReadOnlySpan<int> c1 = new[] { 1, 2, 3 };
ImmutableArray<int> d1 = ImmutableArray.Create(1, 2, 3);
// コレクション式による作成 (C# 12 以降の書き方)
int[] a2 = [1, 2, 3];
List<int> b2 = [1, 2, 3];
ReadOnlySpan<int> c2 = [1, 2, 3];
ImmutableArray<int> d2 = [1, 2, 3];
コレクション式は単純に作成してキャストしているのではなく、ターゲットの型に応じて最適な方法で作成しているようです。
(そのため、現状では明示的な型が必須になっていますが…)
.NET 8 ではコレクション式を優先して使用するような規則 (IDE0300, IDE0303, ...) が既定で有効になっています。
また、コレクション式で使用可能なスプレッド演算子 ..
も併せて追加されました。
これを使用することで、以下の様に(JavaScript 等と同じように)コレクションを展開して新しいコレクションを作成できるようになりました。
IEnumerable<int> a = [1, 2];
List<int> b = [4, 5];
int[] c = [0, ..a, 3, ..b];
// [0, 1, 2, 3, 4, 5]
(自動フォーマットにおける、スプレッド演算子の後ろの空白を制御する設定はあるのか?)
任意の型の別名設定
using
ディレクティブを使用することで、任意の型に別名を設定できるようになりました。
これにより、タプル型、配列型、ポインタ型、またはその他の安全でない型に対してエイリアスを作成できます。
using Foo = (int X, int Y);
Foo p1 = (1, 2);
Foo p2 = (3, 4);
既定のラムダパラメータ
ラムダ式に対して、オプション引数や params
引数が使用できるようになりました。
var foo = (int x = 1, params int[] y) =>
Console.WriteLine($"X = {x}, Y = [{string.Join(", ", y)}]");
foo.Invoke(); // X = 1, Y = []
foo.Invoke(2, 3, 4); // X = 2, Y = [3, 4]
ただし、この既定値や params
の情報は型としてあるものの、これらの拘束力は弱いようです。
つまり、以下の様にこれらが異なるラムダ式の代入が可能です。
警告は発生しますし、あまり気にすることはないかもですが…。
// 既定値が異なるラムダ式の代入が可能(警告 (CS9099) は発生する)
Foo foo = (int x = 10) => { };
delegate void Foo(int x = 1);