§1 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();
  }
}
Copyright© 2016 . Released under the WTFPL version 2.
実行結果
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();
  }
}
Copyright© 2016 . Released under the WTFPL version 2.
実行結果
Add(-1)
指定された引数は、有効な値の範囲内にありません。
パラメーター名: 値が負数
3, 1, 5, 2, 4,
1, 2, 3, 4, 5,
5, 4, 3, 2, 1,

§2 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();
  }
}
Copyright© 2016 . Released under the WTFPL version 2.
実行結果
Alice, Bob, Charlie, Dave, Eve, 
Remove 'Charlie'
NotSupportedException
Contains 'Charlie': True
Alice, Bob, Dave, Eve, 
Contains 'Charlie': False