2016-03-13T00:47:59の更新内容

programming/netfx/collections_specialized/ordereddictionary/index.wiki.txt

current previous
1,356 0,0
+
${smdncms:title,OrderedDictionaryクラス}
+
${smdncms:header_title,OrderedDictionaryクラス (System.Collections.Specialized)}
+
${smdncms:keywords,System.Collections.Specialized,OrderedDictionary}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
+

          
+
&msdn(netfx,type,System.Collections.Specialized.OrderedDictionary){System.Collections.Specialized.OrderedDictionaryクラス};はインデックスによるアクセスとキーによるアクセスの両方をサポートするディクショナリ(ハッシュテーブル)です。 OrderedDictionaryに類似したディクショナリを構成するクラスとして[[Hashtable>programming/netfx/collections/1_nongeneric_2_hashtable]]や[[Dictionary<TKey, TValue>>programming/netfx/collections/2_generic_2_dictionary]]、[[SortedDictionary<TKey, TValue>>programming/netfx/collections/2_generic_3_sortedlist_sorteddictionary]]がありますが、これらのクラスと比較するとOrderedDictionaryには次のような違いがあります。
+

          
+
|*ディクショナリを構成する各クラスの特徴
+
|~特徴|~OrderedDictionary|~[[Hashtable>programming/netfx/collections/1_nongeneric_2_hashtable]]|~[[Dictionary>programming/netfx/collections/2_generic_2_dictionary]]|~[[SortedDictionary>programming/netfx/collections/2_generic_3_sortedlist_sorteddictionary]]|h
+
|~インデックスによるアクセス|できる|できない|できない|できる|
+
|~列挙したときの順序|要素を追加した順|(不定)|(不定)|ソートされた順序|
+
|~ジェネリック|>|非ジェネリック型|>|ジェネリック型|
+
|~キー/値のペアを格納する型|>|&msdn(netfx,type,System.Collections.DictionaryEntry){DictionaryEntry};|>|&msdn(netfx,type,System.Collections.Generic.KeyValuePair`2){KeyValuePair};|
+

          
+
OrderedDictionaryは、インデックスによるアクセスができるディクショナリが必要な場合、特に''追加した順序を維持するようなディクショナリ''が必要な場合に有用なコレクションクラスです。 ただし、OrderedDictionaryは非ジェネリックなコレクションクラスであるため、安全に扱うには注意を要します。
+

          
+
追加したときの順序が維持されることが重要で、キーによるアクセスが不要なのであれば``List<KeyValuePair<TKey, TValue>>``、インデックスによるアクセスができれば列挙した時の順序は特に意識しないのであれば``SortedDictionary<TKey, TValue>``によって代用することができます。
+

          
+
#relevantdocs
+

          
+
-[[programming/netfx/collections/1_nongeneric_2_hashtable]]
+
-[[programming/netfx/collections/2_generic_2_dictionary]]
+
-[[programming/netfx/collections/2_generic_3_sortedlist_sorteddictionary]]
+
-[[programming/netfx/collections/3_objectmodel_1_collection]]
+

          
+
#relevantdocs-end
+

          
+
#adunit
+

          
+
*OrderedDictionary
+
OrderedDictionaryはインデックスによるアクセスが可能なディクショナリです。 名前の通り''順序''を持ったディクショナリで、要素を追加すると個々の要素に追加した順でインデックスが割り振られます。 OrderedDictionaryに格納される''値''は、''キー''と''インデックス''の両方で参照できるとも言えます。
+

          
+
#matrix
+
|*OrderedDictionaryのイメージ
+
|~インデックス|~キー|~値|h
+
|0|"foo"|36|
+
|1|"bar"|7|
+
|2|"foo"|42|
+
|:&br;:|:&br;:|:&br;:|
+
#matrix-end
+

          
+
OrderedDictionaryへの要素の追加・設定はHashtableクラスやDictionaryクラスと同様に、インデクサあるいは&msdn(netfx,member,System.Collections.Specialized.OrderedDictionary.Add){Addメソッド};によって行います。 一方、キーだけでなくインデックスによって対応する値にアクセスできる点、foreachなどによって列挙した場合にインデックスの順(=追加された順)で列挙されることがクラスの動作として保証されている点が、HashtableやDictionaryとは異なります。
+

          
+
#tabpage(codelang=cs,container-title=OrderedDictionaryへの要素の追加、OrderedDictionaryでの要素の列挙)
+
#code{{
+
using System;
+
using System.Collections; // DictionaryEntryを使うのに必要
+
using System.Collections.Specialized;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // インスタンスの作成
+
    var dict = new OrderedDictionary();
+

          
+
    // 要素の追加
+
    dict.Add("foo", 16); // インデックス0の要素として追加される
+
    dict.Add("bar", 72); // インデックス1の要素として追加される
+

          
+
    // 要素の設定
+
    dict["baz"] = 42; // 新たにキー"baz"で要素を追加、インデックス2の要素として追加される
+
    dict["foo"] = 36; // 既存のキー"foo"に対応する値を変更
+

          
+
    dict[1] = 7; // インデックス1(キー"bar")に対応する値を7に変更
+

          
+
    // キーとそれに対応する値を列挙する
+
    foreach (DictionaryEntry pair in dict) {
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
+
    }
+

          
+
    // インデックスとそれに対応する値を列挙する
+
    for (var index = 0; index < dict.Count; index++) {
+
      Console.WriteLine("{0} => {1}", index, dict[index]);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections ' DictionaryEntryを使うのに必要
+
Imports System.Collections.Specialized
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' インスタンスの作成
+
    Dim dict As New OrderedDictionary()
+

          
+
    ' 要素の追加
+
    dict.Add("foo", 16) ' インデックス0の要素として追加される
+
    dict.Add("bar", 72) ' インデックス1の要素として追加される
+

          
+
    ' 要素の設定
+
    dict("baz") = 42 ' 新たにキー"baz"で要素を追加、インデックス2の要素として追加される
+
    dict("foo") = 36 ' 既存のキー"foo"に対応する値を変更
+

          
+
    dict(1) = 7 ' インデックス1(キー"bar")に対応する値を7に変更
+

          
+
    ' キーとそれに対応する値を列挙する
+
    For Each pair As DictionaryEntry In dict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
+
    Next
+

          
+
    ' インデックスとそれに対応する値を列挙する
+
    For index As Integer = 0 To dict.Count - 1
+
      Console.WriteLine("{0} => {1}", index, dict(index))
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo => 36
+
bar => 7
+
baz => 42
+
0 => 36
+
1 => 7
+
2 => 42
+
}}
+

          
+
#remarks
+
Dictionaryクラスでは、''列挙される際の順序は未定義''とされていて、要素が追加された順序になるとは限らない旨が記載されています。 詳しくは[[programming/netfx/collections/2_generic_2_dictionary#ordered_enumeration]]を参照してください。
+
#remarks-end
+

          
+
*インデックスを指定した挿入・削除
+
OrderedDictionaryでは個々の要素がインデックスを持つことから、インデックスを指定することによって位置を指定した挿入・削除を行うことができます。
+

          
+
**挿入 (Insert) [#OrderedDictionary.Insert]
+
&msdn(netfx,member,System.Collections.Specialized.OrderedDictionary.Insert){Insertメソッド};を使うと、インデックスで指定した位置へ要素を挿入することができます。 この操作はインデックスを割り当てた上での追加と見ることもできます。 インデックスとして現在の要素数(=最後のインデックス+1)を指定すれば、末尾へと挿入することができます。 それを超える範囲に追加しようとした場合は例外&msdn(netfx,type,System.ArgumentOutOfRangeException){ArgumentOutOfRangeException};がスローされます。
+

          
+
#tabpage(codelang=cs,container-title=Insertメソッドを使ってOrderedDictionaryの指定した位置へ要素を追加する)
+
#code{{
+
using System;
+
using System.Collections;
+
using System.Collections.Specialized;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var dict = new OrderedDictionary();
+

          
+
    dict.Add("foo", 16);
+
    dict.Add("bar", 72);
+
    dict.Add("baz", 42);
+

          
+
    // インデックス1にキー"qux"・値7の要素を挿入する
+
    dict.Insert(1, "qux", 7);
+

          
+
    // 末尾に要素を挿入(追加)する
+
    dict.Insert(dict.Count, "quux", 89);
+

          
+
    foreach (DictionaryEntry pair in dict) {
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections
+
Imports System.Collections.Specialized
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New OrderedDictionary()
+

          
+
    dict.Add("foo", 16)
+
    dict.Add("bar", 72)
+
    dict.Add("baz", 42)
+

          
+
    ' インデックス1にキー"qux"・値7の要素を挿入する
+
    dict.Insert(1, "qux", 7)
+

          
+
    ' 末尾に要素を挿入(追加)する
+
    dict.Insert(dict.Count, "quux", 89)
+

          
+
    For Each pair As DictionaryEntry In dict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo => 16
+
qux => 7
+
bar => 72
+
baz => 42
+
quux => 89
+
}}
+

          
+
**削除 (RemoveAt) [#OrderedDictionary.RemoveAt]
+
&msdn(netfx,member,System.Collections.Specialized.OrderedDictionary.RemoveAt){RemoveAtメソッド};を使うと、インデックスで指定した位置にある要素を削除することができます。 要素の削除を行うと、それに伴い要素に割り当てられるインデックスも変わります。 削除を行っても追加された際の''前後関係''は維持されますが、インデックスは変化します。
+

          
+
#tabpage(codelang=cs,container-title=RemoveAtメソッドを使ってOrderedDictionaryの指定した位置にある要素を削除する)
+
#code{{
+
using System;
+
using System.Collections;
+
using System.Collections.Specialized;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var dict = new OrderedDictionary();
+

          
+
    dict.Add("foo", 16);
+
    dict.Add("bar", 72);
+
    dict.Add("baz", 42);
+

          
+
    // インデックス1にある要素(キー"bar"の要素)を削除する
+
    dict.RemoveAt(1);
+

          
+
    foreach (DictionaryEntry pair in dict) {
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
+
    }
+

          
+
    for (var index = 0; index < dict.Count; index++) {
+
      Console.WriteLine("{0} => {1}", index, dict[index]);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections
+
Imports System.Collections.Specialized
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New OrderedDictionary()
+

          
+
    dict.Add("foo", 16)
+
    dict.Add("bar", 72)
+
    dict.Add("baz", 42)
+

          
+
    ' インデックス1にある要素(キー"bar"の要素)を削除する
+
    dict.RemoveAt(1)
+

          
+
    For Each pair As DictionaryEntry In dict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
+
    Next
+

          
+
    For index As Integer = 0 To dict.Count - 1
+
      Console.WriteLine("{0} => {1}", index, dict(index))
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo => 16
+
baz => 42
+
0 => 16
+
1 => 42
+
}}
+

          
+

          
+

          
+

          
+
*インデックスに対応するキーの取得
+
&msdn(netfx,member,System.Collections.Specialized.OrderedDictionary.Keys){OrderedDictionary.Keysプロパティ};は&msdn(netfx,type,System.Collections.ICollection){ICollection};となっているため、このプロパティを使ってインデックスに対応するキーを取得するといったことができません。
+

          
+
こういった操作を行う場合は、Keysプロパティの内容を&msdn(netfx,member,System.Collections.ICollection.CopyTo){CopyToメソッド};によっていったん配列へとコピーしてから行う必要があります。
+

          
+
#tabpage(codelang=cs,container-title=Keys.CopyToメソッドを使ってキーを配列にコピーし、インデックスに対応するキーを取得する)
+
#code{{
+
using System;
+
using System.Collections;
+
using System.Collections.Specialized;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var dict = new OrderedDictionary();
+

          
+
    dict.Add("foo", 16);
+
    dict.Add("bar", 72);
+
    dict.Add("baz", 42);
+

          
+
    // このようにしてインデックスに対応するキーを取得することはできない
+
    //var key = dict.Keys[0];
+

          
+
    // OrderedDictionaryのキーを配列にコピーする
+
    var keys = new string[dict.Count];
+

          
+
    dict.Keys.CopyTo(keys, 0);
+

          
+
    // インデックスを使ってキーの配列を列挙する
+
    Console.WriteLine("[Keys]");
+

          
+
    for (var index = 0; index < keys.Length; index++) {
+
      Console.WriteLine("{0} => {1}", index, keys[index]);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections
+
Imports System.Collections.Specialized
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New OrderedDictionary()
+

          
+
    dict.Add("foo", 16)
+
    dict.Add("bar", 72)
+
    dict.Add("baz", 42)
+

          
+
    ' このようにしてインデックスに対応するキーを取得することはできない
+
    'Dim key As String = dict.Keys(0)
+

          
+
    ' OrderedDictionaryのキーを配列にコピーする
+
    Dim keys(dict.Count - 1) As String
+

          
+
    dict.Keys.CopyTo(keys, 0)
+

          
+
    ' インデックスを使ってキーの配列を列挙する
+
    Console.WriteLine("[Keys]")
+

          
+
    For index As Integer = 0 To keys.Length - 1
+
      Console.WriteLine("{0} => {1}", index, keys(index))
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
[Keys]
+
0 => foo
+
1 => bar
+
2 => baz
+
}}
+

          
+

          
+

          
+
*IOrderedDictionaryインターフェイス
+
OrderedDictionaryは&msdn(netfx,type,System.Collections.Specialized.IOrderedDictionary){IOrderedDictionaryインターフェイス};を実装しています。 このインターフェイスは&msdn(netfx,type,System.Collections.IDictionary){IDictionaryインターフェイス};を継承しています。
+

          
+
IOrderedDictionaryには、IDictionaryの持つ機能に加えて、インデックスおよびキーを指定することができるインデクサ、[[Insertメソッド>#OrderedDictionary.Insert]]、[[RemoveAtメソッド>#OrderedDictionary.RemoveAt]]の定義が追加されています。
+

          
+
.NET Frameworkのクラスライブラリでは、IOrderedDictionaryインターフェイスが実装されているクラスはOrderedDictionary以外に存在せず、System.Web.dllの一部で戻り値や引数として周辺でIOrderedDictionaryが使われている程度です。
+

          
+

          
+

          
+
*ジェネリックバージョンのOrderedDictionary
+
[[Hashtable>programming/netfx/collections/1_nongeneric_2_hashtable]]のジェネリック版として対応する[[Dictionary<TKey, TValue>>programming/netfx/collections/2_generic_2_dictionary]]のように、OrderedDictionaryのジェネリック版として対応するようなOrderedDictionary<TKey, TValue>といったクラスは.NET Framework 4.6時点でも存在しません。 必要な場合は独自に実装する必要があります。
+

          
+

          

programming/netfx/tips/generic_ordereddictionary/index.wiki.txt

current previous
1,606 0,0
+
${smdncms:title,ジェネリック版OrderedDictionary}
+
${smdncms:keywords,C#,OrderedDictioanry,ジェネリック}
+
${smdncms:tags,lang/c#,api/.net}
+

          
+
&msdn(netfx,type,System.Collections.Specialized.OrderedDictionary){System.Collections.Specialized.OrderedDictionaryクラス};は非ジェネリックで、これに対応するジェネリック版のクラスが存在しない。
+

          
+
以下は&msdn(netfx,type,System.Collections.ObjectModel.Collection`1){System.Collections.ObjectModel.Collection};<&msdn(netfx,type,System.Collections.Generic.KeyValuePair`2){System.Collections.Generic.KeyValuePair<TKey, TValue>};>をベースにOrderedDictionary<TKey, TValue>を実装したもの。
+

          
+
#relevantdocs
+

          
+
-[[programming/netfx/collections_specialized/ordereddictionary]]
+
-[[programming/netfx/collections/2_generic_2_dictionary]]
+
-[[programming/netfx/collections/2_generic_3_sortedlist_sorteddictionary]]
+
-[[programming/netfx/collections/3_objectmodel_1_collection]]
+

          
+
#relevantdocs-end
+

          
+
#adunit
+

          
+
*使用例・動作例
+
#code(cs,type=unittest,使用例){{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var dict = new OrderedDictionary<string, int>();
+

          
+
    dict.Add("foo", 16);
+

          
+
    dict["bar"] = 72;
+

          
+
    dict.Add("qux", 7);
+

          
+
    dict.Insert(2, "baz", 42);
+

          
+
    for (var index = 0; index < dict.Count; index++) {
+
      Console.WriteLine("{0} => {{{1}, {2}}}",
+
                        index,
+
                        dict[index].Key,
+
                        dict[index].Value);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
0 => {foo, 16}
+
1 => {bar, 72}
+
2 => {baz, 42}
+
3 => {qux, 7}
+
}}
+

          
+
*OrderedDictionary<TKey, TValue>
+
実装となるコード。 &msdn(netfx,type,System.Collections.Generic.Dictionary`2);と同じ動作となるようにしてあるので、これを置き換える目的であればコードの修正は必要ないが、&msdn(netfx,type,System.Collections.Specialized.OrderedDictionary);を置き換える目的で使用する場合は、使用側のコードを修正する必要がある。
+

          
+
その他注意事項・制限事項等はソース中のコメントを参照のこと。 ライセンスは[[MIT License>misc/license/MIT]]とします(ソース下部のリンク参照)。
+

          
+
#code(cs,license=mit,OrderedDictionary<TKey, TValue>){{
+
#define ENABLE_KEYS_VALUES
+
//#undef ENABLE_KEYS_VALUES
+

          
+
using System;
+
using System.Linq;
+
using System.Collections.Generic;
+

          
+
/// <summary>
+
/// ジェネリック版のOrderedDictionary実装。
+
/// </summary>
+
/// <remarks>
+
///  <para>
+
///   System.Collections.ObjectModel.Collection&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;をベースとして構築したOrderedDictionary。
+
///  </para>
+
///  <para>
+
///   このクラスにおける制限事項等は次の通り。
+
///   <list type="bullet">
+
///    <item>
+
///     <description>
+
///      インデクサthis[int index]の型はTValueではなく、KeyValuePair&lt;TKey, TValue&gt;。
+
///     つまりインデックスによるアクセスでは値を取得・設定するのではなく、KeyValuePairを取得・設定することになる。
+
///     </description>
+
///    </item>
+
///    <item><description>キーによるアクセスはO(n)、インデックスによるアクセスはO(1)となる。</description></item>
+
///    <item><description>容量(Capacity)を指定することはできない。</description></item>
+
///    <item><description>ISerializableを実装していない。 シリアライズの動作が未定義。</description></item>
+
///    <item>
+
///     <description>
+
///      Keysプロパティ・Valuesプロパティは、アクセス時にキャッシュされたコレクションを別途生成する。
+
///      このプロパティの実装が不要な場合はENABLE_KEYS_VALUESをundefにすることで無効化できる。
+
///     </description>
+
///    </item>
+
///   </list>
+
///  </para>
+
/// </remarks>
+
public class OrderedDictionary<TKey, TValue> :
+
  System.Collections.ObjectModel.Collection<KeyValuePair<TKey, TValue>>,
+
  IDictionary<TKey, TValue>
+
{
+
  private readonly IEqualityComparer<TKey> keyComparer;
+

          
+
  public OrderedDictionary()
+
    : this(EqualityComparer<TKey>.Default)
+
  {
+
  }
+

          
+
  public OrderedDictionary(IEqualityComparer<TKey> keyComparer)
+
  {
+
    this.keyComparer = keyComparer;
+
  }
+

          
+
  public void Add(TKey key, TValue val)
+
  {
+
    Add(new KeyValuePair<TKey, TValue>(key, val));
+
  }
+

          
+
  public void Insert(int index, TKey key, TValue val)
+
  {
+
    Insert(index, new KeyValuePair<TKey, TValue>(key, val));
+
  }
+

          
+
  public bool Remove(TKey key)
+
  {
+
    int index;
+

          
+
    if (TryGetIndex(key, out index)) {
+
      RemoveAt(index);
+

          
+
      return true;
+
    }
+
    else {
+
      return false;
+
    }
+
  }
+

          
+
  public bool ContainsKey(TKey key)
+
  {
+
    int index;
+

          
+
    return TryGetIndex(key, out index);
+
  }
+

          
+
  public bool TryGetValue(TKey key, out TValue val)
+
  {
+
    int index;
+

          
+
    if (TryGetIndex(key, out index)) {
+
      val = this[index].Value;
+

          
+
      return true;
+
    }
+
    else {
+
      val = default(TValue);
+

          
+
      return false;
+
    }
+
  }
+

          
+
  /// <summary>
+
  /// キーに対応するインデックスを取得する。
+
  /// </summary>
+
  /// <returns>キーに該当する要素がある場合は<c>true</c>、ない場合は<c>false</c>。</returns>
+
  /// <param name="key">インデックスを取得したい要素のキー。</param>
+
  /// <param name="index">キーに該当する要素がある場合、そのインデックス。</param>
+
  private bool TryGetIndex(TKey key, out int index)
+
  {
+
    for (index = 0; index < Count; index++) {
+
      if (keyComparer.Equals(this[index].Key, key))
+
        return true;
+
    }
+

          
+
    return false;
+
  }
+

          
+
  public TValue this[TKey key]
+
  {
+
    get
+
    {
+
      int index;
+

          
+
      if (TryGetIndex(key, out index))
+
        return this[index].Value;
+
      else
+
        throw new KeyNotFoundException(string.Format("item not found; key = {0}", key));
+
    }
+
    set
+
    {
+
      int index;
+

          
+
      if (TryGetIndex(key, out index))
+
        this[index] = new KeyValuePair<TKey, TValue>(key, value);
+
      else
+
        Add(key, value);
+
    }
+
  }
+

          
+
#if ENABLE_KEYS_VALUES
+
  private ICollection<TKey> keys = null;
+
  private ICollection<TValue> values = null;
+

          
+
  private bool modified = true;
+

          
+
  /// <summary>
+
  /// キャッシュされたkeys, valuesを最新の状態にする。
+
  /// </summary>
+
  /// <remarks>
+
  /// 前回のキャッシュ生成以降にコレクションが変更されていれば、キャッシュを破棄して生成しなおす。
+
  /// </remarks>
+
  private void EnsureKeysAndValuesUpdated()
+
  {
+
    if (!modified)
+
      return;
+

          
+
    keys = this.Select(pair => pair.Key).ToList().AsReadOnly();
+
    values = this.Select(pair => pair.Value).ToList().AsReadOnly();
+

          
+
    modified = false;
+
  }
+
#endif
+

          
+
  public ICollection<TKey> Keys {
+
    get
+
    {
+
#if ENABLE_KEYS_VALUES
+
      EnsureKeysAndValuesUpdated();
+

          
+
      return keys;
+
#else
+
      throw new NotSupportedException();
+
#endif
+
    }
+
  }
+

          
+
  public ICollection<TValue> Values {
+
    get
+
    {
+
#if ENABLE_KEYS_VALUES
+
      EnsureKeysAndValuesUpdated();
+

          
+
      return values;
+
#else
+
      throw new NotSupportedException();
+
#endif
+
    }
+
  }
+

          
+
  protected override void InsertItem(int index, KeyValuePair<TKey, TValue> item)
+
  {
+
    int existentIndex;
+

          
+
    if (TryGetIndex(item.Key, out existentIndex))
+
      throw new ArgumentException(string.Format("the item already exists; key = {0}", item.Key));
+

          
+
    base.InsertItem(index, item);
+

          
+
#if ENABLE_KEYS_VALUES
+
    modified = true;
+
#endif
+
  }
+

          
+
  protected override void SetItem(int index, KeyValuePair<TKey, TValue> item)
+
  {
+
    int existentIndex;
+

          
+
    if (TryGetIndex(item.Key, out existentIndex) && index != existentIndex)
+
      throw new ArgumentException(string.Format("the item already exists; key = {0}", item.Key));
+

          
+
    base.SetItem(index, item);
+

          
+
#if ENABLE_KEYS_VALUES
+
    modified = true;
+
#endif
+
  }
+

          
+
#if ENABLE_KEYS_VALUES
+
  protected override void RemoveItem(int index)
+
  {
+
    base.RemoveItem(index);
+

          
+
    modified = true;
+
  }
+

          
+
  protected override void ClearItems()
+
  {
+
    base.ClearItems();
+

          
+
    modified = true;
+
  }
+
#endif
+
}
+
}}
+

          
+

          
+
*テストコード
+
要NUnit。 ライセンスは[[MIT License>misc/license/MIT]]とします(ソース下部のリンク参照)。
+

          
+
#code(cs,type=unittest,license=mit,OrderedDictionary<TKey, TValue>のテストコード){{
+
#define ORDERED_DICTIONARY
+
// ORDERED_DICTIONARYをundefするとSystem.Collections.Generic.Dictionaryと同じ動作となることの確認用テストコードとなる
+
//#undef ORDERED_DICTIONARY
+

          
+
using System;
+
using System.Collections.Generic;
+
using NUnit.Framework;
+

          
+
#if ORDERED_DICTIONARY
+
using Dict = OrderedDictionary<string, int>;
+
#else
+
using Dict = System.Collections.Generic.Dictionary<string, int>;
+
#endif
+
using Pair = System.Collections.Generic.KeyValuePair<string, int>;
+

          
+
[TestFixture]
+
public class OrderedDictionaryTests {
+
#if ORDERED_DICTIONARY
+
  [Test]
+
  public void MajorUseCase1()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("foo", 16);
+
    dict.Add("baz", 42);
+

          
+
    dict.Insert(1, "bar", 72);
+

          
+
    CollectionAssert.AreEqual(new[] {"foo", "bar", "baz"}, dict.Keys, "#1");
+
    CollectionAssert.AreEqual(new[] {16, 72, 42}, dict.Values, "#2");
+

          
+
    dict["bar"] = 36;
+

          
+
    Assert.AreEqual(36, dict["bar"]);
+

          
+
    CollectionAssert.AreEqual(new[] {"foo", "bar", "baz"}, dict.Keys, "#3");
+
    CollectionAssert.AreEqual(new[] {16, 36, 42}, dict.Values, "#4");
+

          
+
    dict.Clear();
+

          
+
    CollectionAssert.IsEmpty(dict, "#5");
+
    CollectionAssert.IsEmpty(dict.Keys, "#6");
+
    CollectionAssert.IsEmpty(dict.Values, "#7");
+
  }
+

          
+
  [Test]
+
  public void MajorUseCase2()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("foo", 16);
+
    dict.Add("bar", 72);
+
    dict.Add("baz", 42);
+

          
+
    var expectedKeys = new[] {"foo", "bar", "baz"};
+
    var expectedValues = new[] {16, 72, 42};
+

          
+
    for (var index = 0; index < dict.Count; index++) {
+
      Assert.AreEqual(expectedKeys[index], dict[index].Key, "#1 key of index {0}", index);
+
      Assert.AreEqual(expectedValues[index], dict[index].Value, "#2 value of index {0}", index);
+
    }
+
  }
+
#endif
+

          
+
  [Test]
+
  public void TestEqualityComparer()
+
  {
+
    var dict = new Dict(StringComparer.OrdinalIgnoreCase);
+

          
+
    dict.Add("a", 1);
+

          
+
    Assert.AreEqual(1, dict["A"], "#1");
+
#if ORDERED_DICTIONARY
+
    Assert.AreEqual("a", dict[0].Key, "#2");
+
#endif
+

          
+
    Assert.Throws<ArgumentException>(() => dict.Add("A", 2), "#3");
+
  }
+

          
+
  [Test]
+
  public void TestSetByKey()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.DoesNotThrow(() => dict["a"] = 1, "#1 new entry");
+
    Assert.AreEqual(1, dict["a"], "#2");
+

          
+
    Assert.DoesNotThrow(() => dict["a"] = 2, "#3 existent entry");
+
    Assert.AreEqual(2, dict["a"], "#4");
+
  }
+

          
+
#if ORDERED_DICTIONARY
+
  [Test]
+
  public void TestSetByIndex()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.Throws<ArgumentOutOfRangeException>(() => dict[0] = new Pair("a", 1), "#1 new entry");
+

          
+
    dict.Add("a", 1);
+

          
+
    Assert.DoesNotThrow(() => dict[0] = new Pair("b", 2), "#2 existent entry");
+

          
+
    Assert.AreEqual(1, dict.Count, "#3");
+
    Assert.AreEqual(2, dict["b"], "#4");
+
  }
+

          
+
  [Test]
+
  public void TestSetByIndexExistentKey()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("a", 1);
+
    dict.Add("b", 2);
+

          
+
    Assert.Throws<ArgumentException>(() => dict[0] = new Pair("b", 3), "#1 existent key");
+

          
+
    Assert.AreEqual("a", dict[0].Key, "#2");
+
  }
+
#endif
+

          
+
  [Test]
+
  public void TestGetByKey()
+
  {
+
    var dict = new Dict();
+

          
+
    dict["a"] = 1;
+

          
+
    Assert.AreEqual(1, dict["a"], "#1 existent entry");
+
    Assert.Throws<KeyNotFoundException>(() => {int b = dict["b"];}, "#2 non existent entry");
+
  }
+

          
+
#if ORDERED_DICTIONARY
+
  [Test]
+
  public void TestGetByIndex()
+
  {
+
    var dict = new Dict();
+

          
+
    dict["a"] = 1;
+

          
+
    Assert.AreEqual("a", dict[0].Key, "#1 existent entry");
+
    Assert.AreEqual(1, dict[0].Value, "#2 existent entry");
+
    Assert.Throws<ArgumentOutOfRangeException>(() => {var pair = dict[1];}, "#3 non existent entry");
+
  }
+
#endif
+

          
+
  [Test]
+
  public void TestAddKeyValue()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.DoesNotThrow(() => dict.Add("a", 1), "#1 add new");
+

          
+
    Assert.AreEqual(1, dict.Count, "#2");
+
    Assert.AreEqual(1, dict["a"], "#3");
+
#if ORDERED_DICTIONARY
+
    Assert.AreEqual("a", dict[0].Key, "#4");
+
    Assert.AreEqual(1, dict[0].Value, "#5");
+
#endif
+

          
+
    Assert.Throws<ArgumentException>(() => dict.Add("a", 2), "#6 add existent key");
+
#if ORDERED_DICTIONARY
+
    Assert.Throws<ArgumentException>(() => dict.Add(new Pair("a", 3)), "#7 add existent key");
+
#endif
+
  }
+

          
+
#if ORDERED_DICTIONARY
+
  [Test]
+
  public void TestInsertKeyValue()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("a", 1);
+

          
+
    Assert.DoesNotThrow(() => dict.Insert(0, "b", 2), "#1 insert new");
+

          
+
    Assert.AreEqual(2, dict.Count, "#2");
+
    Assert.AreEqual(2, dict["b"], "#3");
+
    Assert.AreEqual("b", dict[0].Key, "#4");
+
    Assert.AreEqual(2, dict[0].Value, "#5");
+

          
+
    Assert.Throws<ArgumentException>(() => dict.Insert(0, "a", 2), "#6 insert existent key");
+
    Assert.Throws<ArgumentException>(() => dict.Insert(1, "a", 2), "#7 insert existent key");
+
    Assert.Throws<ArgumentException>(() => dict.Insert(0, new Pair("a", 3)), "#8 add existent key");
+
    Assert.Throws<ArgumentException>(() => dict.Insert(1, new Pair("a", 3)), "#9 add existent key");
+

          
+
    Assert.Throws<ArgumentOutOfRangeException>(() => dict.Insert(3, "c", 3), "#10 insert to out of range");
+
    Assert.Throws<ArgumentOutOfRangeException>(() => dict.Insert(-1, "c", 3), "#11 insert to out of range");
+
  }
+
#endif
+

          
+
  [Test]
+
  public void TestRemoveByKey()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("a", 1);
+

          
+
    Assert.IsFalse(dict.Remove("b"), "#1 remove non existent key");
+

          
+
    Assert.AreEqual(1, dict.Count, "#2");
+

          
+
    Assert.IsTrue(dict.Remove("a"), "#3 remove existent key");
+

          
+
    Assert.AreEqual(0, dict.Count, "#4");
+
  }
+

          
+
  [Test]
+
  public void TestContainsKey()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("a", 1);
+

          
+
    Assert.IsFalse(dict.ContainsKey("b"), "#1 non existent key");
+
    Assert.IsTrue(dict.ContainsKey("a"), "#2 existent key");
+
  }
+

          
+
  [Test]
+
  public void TestTryGetValue()
+
  {
+
    var dict = new Dict();
+

          
+
    dict.Add("a", 1);
+

          
+
    int val;
+

          
+
    Assert.IsFalse(dict.TryGetValue("b", out val), "#1 non existent key");
+

          
+
    Assert.IsTrue(dict.TryGetValue("a", out val), "#2 existent key");
+
    Assert.AreEqual(1, val, "#3");
+
  }
+

          
+
#if ORDERED_DICTIONARY
+
  [Test]
+
  public void TestKeysReadOnly()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.Throws<NotSupportedException>(() => dict.Keys.Add("x"), "#1");
+
  }
+

          
+
  [Test]
+
  public void TestValuesReadOnly()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.Throws<NotSupportedException>(() => dict.Values.Add(-1), "#1");
+
  }
+
#endif
+

          
+
  [Test]
+
  public void TestKeys()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.IsNotNull(dict.Keys, "#1-1 initial state");
+
    CollectionAssert.IsEmpty(dict.Keys, "#1-2 initial state");
+

          
+
    dict.Add("a", 1);
+

          
+
    CollectionAssert.AreEqual(new[] {"a"}, dict.Keys, "#2-1 after insert item");
+

          
+
    dict.Add("b", 2);
+

          
+
    CollectionAssert.AreEqual(new[] {"a", "b"}, dict.Keys, "#2-2 after insert item");
+

          
+
    dict["a"] = 0;
+

          
+
    CollectionAssert.AreEqual(new[] {"a", "b"}, dict.Keys, "#3 after set item");
+

          
+
    dict.Remove("a");
+

          
+
    CollectionAssert.AreEqual(new[] {"b"}, dict.Keys, "#3 after remove item");
+

          
+
    dict.Clear();
+

          
+
    CollectionAssert.IsEmpty(dict.Keys, "#4 after clear");
+
  }
+

          
+
  [Test]
+
  public void TestValues()
+
  {
+
    var dict = new Dict();
+

          
+
    Assert.IsNotNull(dict.Values, "#1-1 initial state");
+
    CollectionAssert.IsEmpty(dict.Values, "#1-2 initial state");
+

          
+
    dict.Add("a", 1);
+

          
+
    CollectionAssert.AreEqual(new[] {1}, dict.Values, "#2-1 after insert item");
+

          
+
    dict.Add("b", 2);
+

          
+
    CollectionAssert.AreEqual(new[] {1, 2}, dict.Values, "#2-2 after insert item");
+

          
+
    dict["a"] = 0;
+

          
+
    CollectionAssert.AreEqual(new[] {0, 2}, dict.Values, "#3 after set item");
+

          
+
    dict.Remove("a");
+

          
+
    CollectionAssert.AreEqual(new[] {2}, dict.Values, "#3 after remove item");
+

          
+
    dict.Clear();
+

          
+
    CollectionAssert.IsEmpty(dict.Values, "#4 after clear");
+
  }
+
}
+
}}