コレクション式 - C# 言語リファレンス
コレクション式を使用して、共通のコレクション値を作成できます。 コレクション式は、評価時に、さまざまなコレクション型に割り当てることができる簡潔な構文です。 コレクション式には、[
と ]
の括弧の間の一連の要素が含まれます。 次の例では、string
要素のうち System.Span<T> を宣言し、曜日に初期化しています。
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
コレクション式は、さまざまなコレクション型に変換できます。 最初の例では、コレクション式を使用して変数を初期化する方法について説明しています。 次のコードは、コレクション式を使用できる他の多くの場所を示しています。
// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// property with expression body:
public IEnumerable<int> MaxDays =>
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
public int Sum(IEnumerable<int> values) =>
values.Sum();
public void Example()
{
// As a parameter:
int sum = Sum([1, 2, 3, 4, 5]);
}
コレクション式は、定数の初期化やメソッドの引数の規定値など、コンパイル時の定数が想定されるところでは使用できません。
これまでの例では、いずれもコレクション式の要素として定数を使用していました。 次の例に示すように、要素に変数を使用することもできます。
string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
Console.WriteLine(element);
}
Spread 要素
Spread 要素 ..
を使用して、コレクション式のコレクション値をインライン化します。 次の例では、以下のいずれかにできる母音のコレクション、子音のコレクション、文字 "y "を組み合わせて、アルファベット全体のコレクションを作成しています。
string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];
Spread 要素 ..vowels
は、評価時に、以下の 5 つの要素が生成されます: "a"
、"e"
、"i"
、"o"
、および "u"
。 Spread 要素 ..consonants
は、consonants
配列の数である 20 個の要素を生成します。 Spread 要素の変数は、foreach
ステートメントを使用して列挙できる必要があります。 前の例に示したように、コレクション式で Spread 要素と個々の要素を組み合わせることができます。
変換
コレクション式は、次のようなさまざまなコレクション型に変換できます。
- System.Span<T> および System.ReadOnlySpan<T>。
- 配列。
- create メソッドを持つすべての型のパラメーター型は、コレクション式型から
T
への暗黙の型変換があるReadOnlySpan<T>
です。 - System.Collections.Generic.List<T> などのコレクション初期化子をサポートする型。 通常、この要件は、型で System.Collections.Generic.IEnumerable<T> がサポートされ、コレクションに項目を追加するためのアクセス可能な
Add
メソッドがあることを意味します。 コレクション式要素の型からコレクションの要素型への暗黙の型変換が行われる必要があります。 Spread 要素の場合、Spread 要素の型からコレクションの要素型への暗黙の型変換が行われる必要があります。 - 次のいずれかのインターフェイスです。
重要
コレクション式は、変換のターゲットの種類に関係なく、コレクション式のすべての要素を含むコレクションを常に作成します。 たとえば、変換のターゲットが System.Collections.Generic.IEnumerable<T> の場合、生成されたコードはコレクション式を評価し、結果をメモリ内コレクションに保存します。
この動作は、シーケンスが列挙されるまでインスタンス化されない可能性がある LINQ とは異なります。 コレクション式を使用して、列挙されない無限シーケンスを生成することはできません。
コンパイラは、スタティック分析を使用して、コレクション式で宣言されたコレクションを作成する最もパフォーマンスの高い方法を判断します。 たとえば、空のコレクション式 []
は、初期化後にターゲットが変更されなければ Array.Empty<T>() として実現できます。 ターゲットが System.Span<T> または System.ReadOnlySpan<T> である場合、ストレージはスタック割り当て可能です。 コレクション式の機能仕様では、コンパイラが従う必要がある規則が指定されています。
多くの API は、パラメーターとして複数のコレクション型にオーバーロードされます。 コレクション式はさまざまな式型に変換できるため、これらの API は正しい変換を指定するためにコレクション式のキャストが必要になる場合があります。 以下の変換ルールにより、いくつかの曖昧さが解消されます。
- Span<T>、ReadOnlySpan<T>、または別の
ref struct
型への変換は、非 REF 構造体型への変換よりも適しています。 - インターフェイス型への変換よりも、非インターフェイス型への変換の方が適しています。
コレクション式が Span
または ReadOnlySpan
に変換されると、スパン オブジェクトの安全なコンテキストは、スパンに含まれるすべての要素の安全なコンテキストから取得されます。 詳細なルールについては、コレクション式指定を参照してください。
コレクション ビルダー
コレクション式は、"適切に動作する" すべてのコレクション型で動作します。 適切に動作するコレクションには、次の特性があります。
- カウント可能なコレクションの
Count
またはLength
の値は、列挙時の要素の数と同じ値を生成します。 - System.Collections.Generic 名前空間内の型は副作用のないものと見なされます。 そのため、コンパイラは、このような型が中間値として使用される可能性があるが、それ以外の場合は公開されないシナリオを最適化できます。
- コレクションで適用可能な
.AddRange(x)
メンバーを呼び出すと、x
を反復処理し、その列挙値をすべて.Add
を使用してコレクションに個別に追加するのと同じ最終的な値になります。
.NET ランタイム内のすべてのコレクション型は適切に動作します。
警告
カスタム コレクション型が適切に動作しない場合、コレクション式でそのコレクション型を使用するときの動作は未定義です。
型は、Create()
メソッドを記述子、System.Runtime.CompilerServices.CollectionBuilderAttribute をコレクション型に適用してビルダー メソッドを示すことで、コレクション式をオプトインします。 たとえば、80 文字の固定長バッファーを使用するアプリケーションについて考えてみましょう。 そのクラスは、次のコードのようになります。
public class LineBuffer : IEnumerable<char>
{
private readonly char[] _buffer = new char[80];
public LineBuffer(ReadOnlySpan<char> buffer)
{
int number = (_buffer.Length < buffer.Length) ? _buffer.Length : buffer.Length;
for (int i = 0; i < number; i++)
{
_buffer[i] = buffer[i];
}
}
public IEnumerator<char> GetEnumerator() => _buffer.AsEnumerable<char>().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _buffer.GetEnumerator();
// etc
}
次のサンプルに示すように、コレクション式と一緒に使用します。
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
LineBuffer
型は IEnumerable<char>
を実装しているため、コンパイラはそれを char
つの項目のコレクションとして認識します。 実装されている System.Collections.Generic.IEnumerable<T> インターフェイスの型パラメーターは、要素型を示します。 コレクション式を LineBuffer
オブジェクトに割り当てられるようにするには、アプリケーションに 2 つの内容を追加する必要があります。 まず、以下の Create
メソッドを含むクラスを作成する必要があります。
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
Create
メソッドは LineBuffer
オブジェクトを返し、ReadOnlySpan<char>
型の単一パラメーターを取る必要があります。 ReadOnlySpan
の型パラメーターは、コレクションの要素型と一致する必要があります。 ジェネリック コレクションを返すビルダー メソッドは、ジェネリック ReadOnlySpan<T>
をそのパラメーターとして持つようになります。 メソッドはアクセス可能でかつ static
である必要があります。
最後に、CollectionBuilderAttribute を LineBuffer
のクラス宣言に追加する必要があります。
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
最初のパラメーターは Builder クラスの名前です。 2 番目の属性はビルダー メソッドの名前が指定されます。
.NET