Ver. 12.0
リリース時期 | 2023/11 |
---|---|
同世代技術 |
|
コレクション式
[]
記号を使って配列などの初期化ができるようになりました。
配列だけではなく、コレクション(List<T>
型など)、Span<T>
なども全く同じ書き方で初期化できます。
これをコレクション式(collection expression)と言います。
using System.Collections.Immutable; int[] array = [1, 2, 3]; List<int> list = [1, 2, 3]; Span<int> span = [1, 2, 3]; ReadOnlySpan<int> ros = [1, 2, 3]; ImmutableArray<int> immutable = [1, 2, 3];
また、コレクション式中では、..
を使うことで「別のコレクションの中身の展開」ができます。
これを スプレッド (spread)演算子と言います。
int[] array1 = [1, 2, 3]; int[] array2 = [4, 5, 6]; // 0, 1, 2, 3, 4, 5, 6, 7 int[] combined = [0, ..array1, ..array2, 7];
詳しくは「コレクション式」で説明します。
プライマリ コンストラクター
通常のクラス、構造体に対してプライマリ コンストラクターが使えるようになりました。
class A(int x) { public int X { get; } = x; }
レコード型の方を先に実装してしまったがために混乱があるんですが、 通常クラス・構造体の場合はプライマリ コンストラクター引数からプロパティを自動生成する機能はありません。
また、これに伴い、class C;
というように、メンバーを1つも持たないでいい場合に {}
を書く必要がなくなりました。
詳しくは「プライマリ コンストラクター」で説明します。
using エイリアスに任意の型を書けるように
C# 11 ではエラーになっていた以下のようなコードをコンパイルできるようになりました。
using Primitive = int; using Array = int[]; using Nullable = int?; using Tuple = (int, int);
詳しくは「任意の型に対する using エイリアス」で説明します。
ラムダ式のデフォルト引数
ラムダ式の引数にオプション引数にできる(既定値を与えられる)ようになりました。 また、params 引数も使えるようになりました。
// オプション引数(既定値値指定)。 var f1 = (int x = 1) => 0; // params 引数。 var f2 = (params int[] x) => 0; // 混在も OK。 var f3 = (int x = 1, params int[] y) => 0;
詳しくは「ラムダ式のオプション引数(既定値)と params 引数」で説明します。
ref readonly 引数
ref 引数、in 引数の亜種として、 「書き換えはしないけども、右辺値は受け付けたくない」ということを表す ref readonly 引数というものを導入しました。
// in 引数の代わりに ref readonly 引数。 void m(ref readonly int x) { } m(10); // リテラルは警告に。 var a = 1; var b = 2; m(a + b); // 式も警告に。 // in や ref を付けないのも警告。 m(a); // in を付けると警告が出ない。 m(in a); // in 引数と違って、ref 修飾でも OK。 m(ref a);
ちなみに、呼び出し側の書き方が変わる以外に差はなく、コンパイル結果の挙動は in 引数と全く同じです。 呼び出し側の差は以下の通りです。
呼び方 | in | ref readonly |
---|---|---|
m(ref x) |
警告 | OK |
m(in x) |
OK | OK |
m(x) , m(x + y) , m(123) |
OK | 警告 |
詳しくは「ref readonly 引数」で説明します。
その他
InlineArray
.NET 8 で、InlineArray
属性 (System.Runtime.CompilerServices
名前空間) というものが入って、「値型の固定長配列」みたいなものを作れるようになりました。
using System.Runtime.CompilerServices; // この属性を付けると、 .NET ランタイムが特別扱いして、構造体のサイズを拡大する。 // (コンストラクター引数で Length 指定。) [InlineArray(3)] struct FixedBuffer<T> { private T _value; }
基本的には .NET ランタイム側の機能ですが、
いくつか、C# 側にもこの InlineArray
向けの特殊対応が入っています。
FixedBuffer<string> buffer = new(); // InlineArray に対して直接インデクサーを書ける。 buffer[0] = "zero"; buffer[1] = "one"; // Span/ReadOnlySpan に暗黙的に変換できる。 Span<string> span = buffer; span[2] = "two"; // foreach で列挙できる。 foreach (var x in buffer) { Console.WriteLine(x); }
詳しくは「[雑記] InlineArray」で説明します。
nameof の微修正
nameof
演算子にちょっとした修正が入りました。
C# 11 以前だと、以下の例の最後の行のように、 静的メンバー内から「インスタンス メンバーのインスタンス メンバー」みたいな名前の参照ができなかったようです。
class A { public string? Instance { get; } // これは元から行けた。 public string InstanceM() => nameof(Instance.Length); public static string StaticM1() => nameof(string.Length); public static string StaticM2() => nameof(Instance); // これが今までダメだったらしい。 public static string StaticM() => nameof(Instance.Length); }
これが、C# 12 ではコンパイルできるようになりました。
正直、バグ修正扱い(最新コンパイラーを使うと C# 11 以下でもコンパイルが通るようになる)でもいいレベルだとは思いますが、一応は C# 12 以上限定です。