Collection

System.Collections.ObjectModel.Collectionクラスはジェネリックコレクションの基本クラスとして使えるクラスです。 並べ替えや述語を指定した検索の操作が用意されていない以外は、Listクラスとほぼ同じ操作が出来ます。

using System;
using System.Collections.ObjectModel;

class Sample {
  static void Main()
  {
    Collection<int> col = new Collection<int>();

    // 要素の追加
    col.Add(0);
    col.Add(1);
    col.Add(2);
    col.Add(3);
    col.Add(4);

    Print(col);

    // 要素の削除
    col.Remove(1);
    col.Remove(3);

    Print(col);

    // 特定の位置にある要素を削除
    col.RemoveAt(1);

    Print(col);

    // 特定の位置にある要素を変更
    col[0] = 5;

    Print(col);

    // 特定の位置に要素を挿入
    col.Insert(1, 6);

    Print(col);

    // 値4の要素が含まれているか
    Console.WriteLine("Contains 4: {0}", col.Contains(4));
  }

  static void Print(Collection<int> col)
  {
    foreach (int e in col) {
      Console.Write("{0}, ", e);
    }

    Console.WriteLine();
  }
}
Imports System
Imports System.Collections.ObjectModel

Class Sample
  Shared Sub Main()
    Dim col As New Collection(Of Integer)

    ' 要素の追加
    col.Add(0)
    col.Add(1)
    col.Add(2)
    col.Add(3)
    col.Add(4)

    Print(col)

    ' 要素の削除
    col.Remove(1)
    col.Remove(3)

    Print(col)

    ' 特定の位置にある要素を削除
    col.RemoveAt(1)

    Print(col)

    ' 特定の位置にある要素を変更
    col(0) = 5

    Print(col)

    ' 特定の位置に要素を挿入
    col.Insert(1, 3)

    Print(col)

    ' 値4の要素が含まれているか
    Console.WriteLine("Contains 4: {0}", col.Contains(4))
  End Sub

  Shared Sub Print(ByVal col As Collection(Of Integer))
    For Each e As Integer In col
      Console.Write("{0}, ", e)
    Next

    Console.WriteLine()
  End Sub
End Class
実行結果
0, 1, 2, 3, 4, 
0, 2, 4, 
0, 4, 
5, 4, 
5, 6, 4, 
Contains 4: True

派生クラスから内部コレクションのIList<T>にアクセスするにはItemsプロパティを参照します。 また、次のプロテクトメソッドをオーバーライドすることで、要素の挿入・削除・設定時の動作を拡張出来ます。

オーバーライド可能なメソッド
メソッド メソッドが呼ばれるタイミング
SetItem 内部コレクションの要素を設定しようとしたとき
InsertItem 内部コレクションに要素を挿入しようとしたとき
RemoveItem 内部コレクションから要素を削除しようとしたとき
ClearItems 内部コレクションから全要素を削除しようとしたとき

以下は、Collectionをクラスを継承して0または正の整数のみを格納できるコレクション型を実装する例です。

using System;
using System.Collections.ObjectModel;

class PositiveNumberCollection : Collection<int> {
  protected override void SetItem(int index, int item)
  {
    // 要素が設定される際に値をチェックする
    CheckRange(item);

    base.SetItem(index, item);
  }

  protected override void InsertItem(int index, int item)
  {
    // 要素が設定される際に値をチェックする
    CheckRange(item);

    base.InsertItem(index, item);
  }

  private void CheckRange(int item)
  {
    // 値が負の場合はArgumentOutOfRangeExceptionをスローする
    if (item < 0) throw new ArgumentOutOfRangeException("値が負数");
  }

  public void Sort()
  {
    // IListインターフェイスからArrayListのラッパーを作り、既定のIComparer<int>を使ってソートする
    System.Collections.ArrayList.Adapter(this).Sort(System.Collections.Generic.Comparer<int>.Default);
  }

  public void Reverse()
  {
    // IListインターフェイスからArrayListのラッパーを作りリバースする
    System.Collections.ArrayList.Adapter(this).Reverse();
  }
}

class Sample {
  static void Main()
  {
    PositiveNumberCollection col = new PositiveNumberCollection();

    // 要素の追加
    col.Add(3);
    col.Add(1);
    col.Add(0);
    col.Add(2);
    col.Add(4);

    col[2] = 5;

    try {
      Console.WriteLine("Add(-1)");
      col.Add(-1);
    }
    catch (ArgumentOutOfRangeException ex) {
      Console.WriteLine(ex.Message);
    }

    Print(col);

    // ソート
    col.Sort();

    Print(col);

    // リバース
    col.Reverse();

    Print(col);
  }

  static void Print(PositiveNumberCollection col)
  {
    foreach (int e in col) {
      Console.Write("{0}, ", e);
    }

    Console.WriteLine();
  }
}
Imports System
Imports System.Collections.ObjectModel

Class PositiveNumberCollection
  Inherits Collection(Of Integer)

  Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As Integer)
    ' 要素が設定される際に値をチェックする
    CheckRange(item)

    MyBase.SetItem(index, item)
  End Sub

  Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Integer)
    ' 要素が設定される際に値をチェックする
    CheckRange(item)

    MyBase.InsertItem(index, item)
  End Sub

  Private Sub CheckRange(ByVal item As Integer)
    ' 値が負の場合はArgumentOutOfRangeExceptionをスローする
    If item < 0 Then Throw New ArgumentOutOfRangeException("値が負数")
  End Sub

  Public Sub Sort()
    ' IListインターフェイスからArrayListのラッパーを作り、既定のIComparer<int>を使ってソートする
    System.Collections.ArrayList.Adapter(Me).Sort(System.Collections.Generic.Comparer(Of Integer).Default)
  End Sub

  Public Sub Reverse()
    ' IListインターフェイスからArrayListのラッパーを作りリバースする
    System.Collections.ArrayList.Adapter(Me).Reverse()
  End Sub
End Class

Class Sample
  Shared Sub Main()
    Dim col As New PositiveNumberCollection()

    ' 要素の追加
    col.Add(3)
    col.Add(1)
    col.Add(0)
    col.Add(2)
    col.Add(4)

    col(2) = 5

    Try
      Console.WriteLine("Add(-1)")
      col.Add(-1)
    Catch ex As ArgumentOutOfRangeException
      Console.WriteLine(ex.Message)
    End Try

    Print(col)

    ' ソート
    col.Sort()

    Print(col)

    ' リバース
    col.Reverse()

    Print(col)
  End Sub

  Shared Sub Print(ByVal col As PositiveNumberCollection)
    For Each e As Integer In col
      Console.Write("{0}, ", e)
    Next

    Console.WriteLine()
  End Sub
End Class
実行結果
Add(-1)
指定された引数は、有効な値の範囲内にありません。
パラメーター名: 値が負数
3, 1, 5, 2, 4,
1, 2, 3, 4, 5,
5, 4, 3, 2, 1,

ReadOnlyCollection

System.Collections.ObjectModel.ReadOnlyCollectionクラスは読み取り専用のジェネリックコレクションを作成するためのクラスです。

ReadOnlyCollectionクラスはArray.AsReadOnlyメソッドList.AsReadOnlyメソッドの戻り値として返され、配列・Listを読み取り専用にしたコレクションとして取得することができます。 また、ReadOnlyCollectionクラスのインスタンスを作成する場合は基になるIList<T>をコンストラクタで指定する必要があり、この場合は指定されたIList<T>に対する読み取り操作のみを許可するラッパーとして動作します。

読み取り操作のみを許可するため、Add, Remove, Clearなどのパブリックメソッドのほか、SetItem, RemoveItem, InsertItemなどのプロテクトメソッドも提供されません。 また、基になるIList<T>に変更を加えるとそれをラップするReadOnlyCollectionにも反映されます。 ReadOnlyCollectionをIListインターフェイスなどにキャストしてAdd, Removeなどのメソッドを呼び出した場合は例外NotSupportedExceptionがスローされます。

派生クラスからは、Itemsプロパティを参照することで基になったIList<T>にアクセスすることが出来ます。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

class Sample {
  static void Main()
  {
    List<string> baseList = new List<string>(new string[] {"Alice", "Bob", "Charlie", "Dave", "Eve"});
    ReadOnlyCollection<string> col = new ReadOnlyCollection<string>(baseList);

    Print(col);

    try {
      // ReadOnlyCollection<string>からIList<string>にキャスト
      IList<string> list = col;

      // IList<string>インターフェイスのRemoveメソッドを呼び出す
      Console.WriteLine("Remove 'Charlie'");

      list.Remove("Charlie");
    }
    catch (NotSupportedException) {
      Console.WriteLine("NotSupportedException");
    }

    // 基になっているコレクションに変更を加える
    Console.WriteLine("Contains 'Charlie': {0}", col.Contains("Charlie"));

    baseList.Remove("Charlie");

    Print(col);

    Console.WriteLine("Contains 'Charlie': {0}", col.Contains("Charlie"));
  }

  static void Print(ReadOnlyCollection<string> col)
  {
    foreach (string e in col) {
      Console.Write("{0}, ", e);
    }

    Console.WriteLine();
  }
}
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel

Class Sample
  Shared Sub Main()
    Dim baseList As New List(Of String)(New String() {"Alice", "Bob", "Charlie", "Dave", "Eve"})
    Dim col As New ReadOnlyCollection(Of String)(baseList)

    Print(col)

    Try
      ' ReadOnlyCollection(Of String)からIList(Of String)にキャスト
      Dim list As IList(Of String) = col

      ' IList<string>インターフェイスのRemoveメソッドを呼び出す
      Console.WriteLine("Remove 'Charlie'")

      list.Remove("Charlie")
    Catch ex As NotSupportedException
      Console.WriteLine("NotSupportedException")
    End Try

    ' 基になっているコレクションに変更を加える
    Console.WriteLine("Contains 'Charlie': {0}", col.Contains("Charlie"))

    baseList.Remove("Charlie")

    Print(col)

    Console.WriteLine("Contains 'Charlie': {0}", col.Contains("Charlie"))
  End Sub

  Shared Sub Print(ByVal col As ReadOnlyCollection(Of String))
    For Each e As String In col
      Console.Write("{0}, ", e)
    Next

    Console.WriteLine()
  End Sub
End Class
実行結果
Alice, Bob, Charlie, Dave, Eve, 
Remove 'Charlie'
NotSupportedException
Contains 'Charlie': True
Alice, Bob, Dave, Eve, 
Contains 'Charlie': False