コレクションとは、配列を補足するデータ構造です。
コレクションに関する型は、System.Collections名前空間で宣言されています。
| 名前空間 | |
|---|---|
| System.Collections.ObjectModel | 再利用可能なライブラリのオブジェクト モデルでコレクションとして使用できるクラスが含まれる。
これらのクラスは、プロパティまたはメソッドがコレクションを返す場合に使用する。 |
| System.Collections.Generic | ジェネリック コレクションを定義するインターフェイスとクラスが含まれる。
このコレクションを使用することにより、ユーザーは、汎用的ではない厳密に型指定されたコレクションに比べてタイプ セーフでパフォーマンスが高い、厳密に型指定されたコレクションを作成できるようになる。 |
| System.Collections.Specialized | 特殊な厳密に型指定されたコレクションが含まれる。
たとえば、リンクされたリスト ディクショナリ、ビット ベクター、文字列だけを含むコレクションなどがある。 |
| System.Collections.Concurrent | スレッド セーフなコレクション クラスが含まれる。
複数のスレッドがコレクションに同時にアクセスするときに、これらのコレクション クラスを名前空間および名前空間の対応する型の代わりに使用する必要がある。ただし、拡張メソッドや明示的なインターフェイスの実装を介したコレクション オブジェクトの要素へのアクセスは、スレッドセーフであるという保証はなく、呼び出し元による同期が必要になる場合がある。 |
以下のインターフェイスで、コレクションの列挙、データの格納や作成のための標準的な方法が提供されます。
| インターフェイス | 実装 | 機能 |
|---|---|---|
| IEnumerator | 単純な反復処理の支援 | |
| IDictionaryEnumerator | IEnumerator | |
| IEnumerable | 単純な反復処理を支援する列挙子を提供 | |
| ICollection | IEnumerable | サイズと列挙子、それに同期オブジェクトの提供 |
| IList | ICollection | |
| IDictionary | ICollection | |
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
IEnumerator インターフェイス (System.Collections) | Microsoft Learn
int[] array = { 0, 1 };
IEnumerator e = array.GetEnumerator();
//object obj = e.Current; // InvalidOperationException「列挙は開始していません。MoveNext を呼び出してください。」
bool r0 = e.MoveNext(); // true
object obj0 = e.Current; // 0
bool r1 = e.MoveNext(); // true
object obj1 = e.Current; // 1
bool r2 = e.MoveNext(); // false
object obj2 = e.Current; // InvalidOperationException「列挙は既に完了しています。」
列挙子を、コレクションの次の要素へ進められます。
public bool MoveNext ();IEnumerator.MoveNext Method (System.Collections) | Microsoft Learn
次の要素へ進められたときは、trueが返されます。
このIEnumeratorのインスタンスが取得された後にコレクションが変更されていると、InvalidOperationExceptionが投げられます。
List<int> list = new List<int>(); IEnumerator e = list.GetEnumerator(); bool r0 = e.MoveNext(); // false list.Add(1); bool r1 = e.MoveNext(); // InvalidOperationException「コレクションが変更されました。列挙操作は実行されない可能性があります。」MoveNext - list.cs
Queue<int> queue = new Queue<int>(); IEnumerator e = queue.GetEnumerator(); bool r0 = e.MoveNext(); // false queue.Enqueue(1); bool r1 = e.MoveNext(); // InvalidOperationException「列挙子がインスタンス化されてから、コレクションが変更されました。」MoveNext - queue.cs
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
IEnumerable インターフェイス (System.Collections) | Microsoft Learn
コレクションを反復 (iterate) するための、列挙子 (enumerator) を得られます。
public System.Collections.IEnumerator GetEnumerator ();IEnumerable.GetEnumerator メソッド (System.Collections) | Microsoft Learn
public interface ICollection : IEnumerable
{
int Count { get; }
bool IsSynchronized { get; }
object SyncRoot { get; }
void CopyTo(Array array, int index);
}
ICollection インターフェイス (System.Collections) | Microsoft Learn
public interface IList : ICollection, IEnumerable
{
object this[int index] { get; set; }
bool IsFixedSize { get; }
bool IsReadOnly { get; }
int Add(object value);
void Clear();
bool Contains(object value);
int IndexOf(object value);
void Insert(int index, object value);
void Remove(object value);
void RemoveAt(int index);
}
IList インターフェイス (System.Collections) | Microsoft Learn
これらはジェネリックではない古いクラスであるため、型の安全性とパフォーマンスを考えるならば、新しいジェネリックなコレクション クラスを使用すべきです。
基底クラス
比較
反復子を定義できます。コンテキスト キーワード yield - C# リファレンス | Microsoft Learn
yieldにより反復子として定義するメソッド、演算子、getアクセサの戻り値は、以下のいずれかでなければなりません。コンパイラ エラー CS1624 | Microsoft Learn
| 構文 | 作用 |
|---|---|
yield return expression; |
expressionを返し、呼び出し元へ処理を戻す。再呼び出し後は、この位置から実行が再開される |
yield break; |
反復を終了し、呼び出し元へ処理を戻す |
たとえばメソッドが次のように定義されているとき、
public IEnumerable<int> Method()
{
yield return 1;
Console.Write("a");
yield return 2;
Console.Write("b");
yield break;
Console.Write("c");
}
これを呼び出すと、
IEnumerable<int> a = Method();
foreach (int b in a)
{
Console.Write(b);
}
1、a、2、bの順に出力されます。
foreach ( local_variable_type identifier in expression )
embedded_statement
12.9.5 The foreach statement - Statements - C# language specification | Microsoft Learn
int[] p = { 1, 2, 3 };
foreach (int x in p)
{
Console.Write(x);
}
foreachで処理しているコレクションは、そのブロック内で項目の追加や削除はできません。foreach (Visual Basic では For Each) ブロック内のステートメントによって、反復処理中のコレクションが変更される - 例外のトラブルシューティング : System.InvalidOperationException | Microsoft Learn
List<int> list = new List<int>(new int[] { 1, 2, 3 }); foreach (int item in list) // 2回目のループで、System.InvalidOperationException { // 「コレクションが変更されました。列挙操作は実行されない可能性があります。(Collection was modified; enumeration operation may not execute.)」 list.Remove(item); }
またコレクションの要素に対して、割り当てることもできません。
foreach (int x in new[] { 1, 2, 3 })
{
x = 10; // error CS1656: 'x' は 'foreach 繰り返し変数' であるため、これに割り当てることはできません。
}
次のようなforeachの処理は、
int[] p = { 1, 2, 3 };
foreach (int v in p)
{
// vを用いた文
}
次のように処理するのと同等です。
int[] p = { 1, 2, 3 };
IEnumerator e = p.GetEnumerator();
try
{
while (e.MoveNext())
{
int v = (int)e.Current;
// vを用いた文
}
}
finally
{
IDisposable d = e as IDisposable;
if (d != null) d.Dispose();
}
12.9.5 The foreach statement - Statements - C# language specification | Microsoft Learn
コレクションがIDisposableを実装していることが自明ならば、usingを用いることで簡潔に記述できます。
using (IEnumerator<T> en = collection.GetEnumerator()) { while (en.MoveNext()) { // en.Current; } }List - list.cs
ConcurrentやGenericで定義されているクラスが要件を満たすならば、それを用います。
ICollectionインターフェイスを実装するならば、それのSyncRootプロパティを同期用のオブジェクトとして用います。
ICollection iCollection = collection; lock (iCollection.SyncRoot) { }Remarks - ICollection.SyncRoot Property (System.Collections) | Microsoft Learn SyncRoot - iCollection.SyncRoot
ICollectionを実装するが、そのクラスのプロパティとしてSyncRootを実装していないクラスでは、ICollectionへキャストした上で取得します。c# - List<T> doesn't implements SyncRoot! - Stack Overflow
List<int> list = new List<int>(); object obj = ((ICollection)list).SyncRoot;
ただしConcurrentのクラスからは取得できません。
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
object obj = ((ICollection)queue).SyncRoot; // NotSupportedException「SyncRoot プロパティは、同時コレクションの同期には使用できません。(The SyncRoot property may not be used for the synchronization of concurrent collections.)」
なおArrayListのような非ジェネリック クラスで定義されていた、スレッド セーフなコレクションを返すSynchronized()は、ジェネリック版では廃止されています。