【C#】逆引きコレクション式
C# 12.0で追加された「コレクション式(Collection Expressions)」ですが、意外と使い勝手が多いので逆引きできるようにしてみました。
逆引き表
| 内容 | 書き方 |
|---|---|
| コレクションのキャスト | T2 t2 = [ .. t1]; |
| コレクションの初期化 | TCollection a = []; |
| ディクショナリの空初期化 | Dictionary<K, V> dic = []; |
| コレクション同士の結合 | TCollection c = [ .. a, .. b]; |
| コレクションの空の判定 | collection is [] |
| 同じ値のコレクションを要素数指定して作る | TCollection a = [ .. Enumerable.Repeat(value,10)]; |
| 連番コレクションを作る | TCollection a = [ .. Enumerable.Range(0,10)]; |
| コレクションを分割して前後入替 | c = [ .. c[index .. ], .. c[ .. index] ]; |
| コレクションの先頭や末尾に要素を追加 |
c = [elem, .. c]; // 先頭 c = [ .. c, elem]; // 末尾
|
| コレクションから指定indexの要素を削除 | a = [ .. a[ .. index], .. a[(index+1) .. ] ]; |
| コレクションの指定indexに要素を追加 | a = [ .. a[ .. index], 999/*追加要素*/, .. a[index ..] ]; |
| TakeLast() / SkipLast() の代わり |
var b = a[^count .. ]; // TakeLast() var b = a[ .. ^count]; // SkipLast()
|
| Python等のリスト内包表記 | [.. from item in list where item > 0 select item] |
前提
「コレクション」は,
-
T[](配列) IEnumerable<T>List<T>Span<T>
等のことを言います(詳細)。リストっぽいヤツらのことです!
「コレクション」には「ディクショナリ」は(一部を除いて)含まれません。
(※「ディクショナリ式」というものが提案されているようです)
「Index/Range型」や「リストパターン」はコレクション式ではないのですが記法的に関係しているので混ぜて紹介します。
コレクションのキャスト
コレクション式
//t1は別の型TCollection1
TCollection2 t2 = [ .. t1 ];
暗黙の型変換できないものも同じ書き方でキャストできる。
int[] array = [];
//cast
IEnumerable<int> ienumerable = [ .. array];
IEnumerable<int> ie2 = array; //暗黙の型変換できる
List<int> list = [ .. array];
Span<int> span = [ .. array];
Span<int> span2 = array; //暗黙の型変換できる
古い書き方
古い書き方を見る
int[] array = { 1, 2, 3 };
List<int> list = new List<int> { 1, 2, 3 };
//T[] から List<T>
List<int> list = new List<int>(array);
//T[] から Span<T>
Span<int> span = array.AsSpan();
//List<T> から T[]
int[] array = list.ToArray();
//List<T>からSpan<T>
Span<int> span = list.ToArray().AsSpan();
コレクションの初期化
コレクション式
TCollection a = [];
どんな場合も基本的に[]。
最代入可能なら、[]で空にすることもできる。
List<int> list = [1,2,3];
list = []; //空に
古い書き方
古い書き方を見る
//配列は{}が使えた
int[] array1 = {};
//インターフェイスなんでnewとかできない
IEnumerable<int> ienum1 = Enumerable.Empty<int>();
//newする場合&型が明らかならnew()で省略できる
List<int> list1 = new();
Span<int> span1 = new();
ディクショナリの空初期化
コレクション式はディクショナリの初期化はできないが、例外的に空のディクショナリの初期化だけはできる。
コレクション式
Dictionary<K, V> dic = [];
古い書き方
古い書き方を見る
Dictionary<K, V> dic = new Dictionary<K, V>();
//古い書き方でも省略記法は一応ある
Dictionary<string, int> dic2 = new();
コレクション同士の結合
コレクション式
//a,bはTCollection
TCollection c = [ .. a, .. b];
[..【結合するコレクションA】, ..【結合するコレクションB】]でOK。
//3つでも4つでも
List<T> x = [ .. a, .. b, .. c];
//順番もお好きに
List<T> r = [ .. c, .. b, .. a];
古い書き方
古い書き方を見る
コレクションの種類によってConcat()だったりAppend()だったり、そもそも結合するメソッドが無かったり。
//T[]
T[] combinedArray = array1.Concat(array2).ToArray();
//IEnumerable<T>
IEnumerable<T> combinedCollection = collection1.Concat(collection2);
//List<T>
list1.AddRange(list2);
//ImmutableArray<T>
ImmutableArray<T> combinedArray = array1.Append(array2);
//Span<T>
//..CopyTo()とか使って色々めんどくさいので省略..
コレクションの空の判定
コレクション式※
//TCollection collection
collection is []
正確にはパターンマッチのリストパターン。コレクション式と対応した書き方になっている。
リストパターンではnullチェックも同時に行われる。
nullの場合の判定もしたい場合はこう。
`collection is null or []`
古い書き方
古い書き方を見る
if (array != null && array.Length == 0 )
if (list != null && list.Count == 0)
if (span.Length == 0)
if (text != null && text.Length == 0)
同じ値のコレクションを要素数指定して作る
コレクション式
//valueは要素の共通の値
TCollection a = [ .. Enumerable.Repeat(value,10)];
Enumerable.Repeat()を[]内でspreadするだけでどんな型のコレクションもいける。
古い書き方
古い書き方を見る
int[] array = Enumerable.Repeat(value,10).ToArray();
List<int> list = Enumerable.Repeat(value,10).ToList();
//...
関連
連番コレクションを作る
コレクション式
//0から10の連番コレクションを作る
TCollection a = [ .. Enumerable.Range(0,10)];
古い書き方
古い書き方を見る
int[] array = Enumerable.Range(0,10).ToArray();
List<int> list = Enumerable.Repeat(0,10).ToList();
//...
関連
コレクションを分割して前後入替
コレクション式
TCollection c = [1,2,3,4,5,6];
c = [ .. c[index .. ], .. c[ .. index] ];
コレクションの先頭や末尾に要素を追加
コレクション式
TCol c = [/*...*/];
//先頭に追加
c = [elem, .. c];
//末尾に追加
c = [ .. c, elem];
古い書き方
古い書き方を見る
T[] c = new T[] { /*...*/ };
T elem = /* 新しい要素 */;
// 先頭に追加
T[] newArray1 = new T[c.Length + 1];
newArray1[0] = elem;
Array.Copy(c, 0, newArray1, 1, c.Length);
c = newArray1;
// 末尾に追加
T[] newArray2 = new T[c.Length + 1];
Array.Copy(c, 0, newArray2, 0, c.Length);
newArray2[^1] = elem; // または newArray2[newArray2.Length - 1]
c = newArray2;
コレクションから指定indexの要素を削除
コレクション式
TCollection a = [1,2,3,4,5,6];
int index = 1 /*指定index*/;
a = [ .. a[ .. index], .. a[(index+1) .. ] ];
コレクションの指定indexに要素を追加
コレクション式
TCollection a = [1,2,3,4,5,6];
int index = 1 /*追加するindex*/;
a = [ .. a[ .. index], 999 /*追加要素*/, .. a[index ..] ];
TakeLast() / SkipLast() の代わり
LINQのTakeLast() / SkipLast()と同じようなことをLINQの対象ではないコレクション(Span<T>とか)に対してもできる。
コレクション式※
TCollection a = [1,2,3,4,5,6];
var count = 2;
var b = a[^count .. ]; //5,6
var b = a[ .. ^count]; //1, 2, 3, 4
正確にはこれはコレクション式ではなく、Index/Rangeのインデックスアクセス。
なのでC#8.0から使える。
TakeLast().ToArray() などのようにキャストを伴う場合の代わりにはコレクション式を使う。
TCollectionB b = [ .. a[^count .. ]];
関連
Python等のリスト内包表記
コレクション式
List<int> list = [0, 1, 2, 3];
ImmutableList<int> newlist = [.. from item in list where item > 0 select item];
Console.WriteLine( string.Join(",", newlist) ); // 1,2,3
コレクション式+LINQクエリ式の組み合わせでPythonとかのリスト内包表記風のことができる。
関連
おまけ:LINQメソッドをコレクション式+αで再現表
LINQに対応していない一部のコレクション(例:Span<T>)でも同等のことができる。
| LINQ メソッド | コレクション式での再現方法 | 備考 |
|---|---|---|
| Cast<T> | [ .. source] |
型変換が不要な場合のみ |
| First | source[0] |
空の場合は例外発生 |
| FirstOrDefault | source is [var first, ..] ? first : default |
空の場合 default
|
| Last | source[^1] |
空の場合は例外発生 |
| LastOrDefault | source is [.., var last] ? last : default |
空の場合 default
|
| Take(n) | source[ .. n] |
n が長さを超えると全体 |
| TakeLast(n) | source[^n .. ] |
|
| Skip(n) | source[n .. ] |
n が長さを超えると空 |
| SkipLast(n) | source[ .. ^n] |
n が長さを超えると空 |
| ElementAt(i) | source[i] |
範囲外なら例外発生 |
| ElementAtOrDefault(i) | source is var s && i < s.Length ? s[i] : default |
範囲外なら default
|
| Concat | [ .. source, ..appendCollection] |
複数コレクションを結合 |
| Append(item) | [ .. source, item] |
末尾に要素を追加 |
| Prepend(item) | [item, .. source] |
先頭に要素を追加 |
関連
-
Cast<T>: コレクションのキャスト -
Concat: コレクション同士の結合 -
Append/Prepend: コレクションの先頭や末尾に要素を追加 -
TakeLast(n)/SkipLast(n): TakeLast() / SkipLast() の代わり
Discussion