2012-09-07T23:26:42の更新内容

programming/netfx/collections/2_generic_2_dictionary/index.wiki.txt

current previous
11,280 11,7
 
#googleadunit(banner)
#googleadunit(banner)
 

        

        
 
*Dictionary [#Dictionary]
*Dictionary [#Dictionary]
~
&msdn(netfx,type,System.Collections.Generic.Dictionary`2){System.Collections.Generic.Dictionaryクラス};は''キー''によってコレクション内の要素にアクセスできるコレクションで、''ハッシュテーブル''や''連想配列''と呼ばれるものに相当します。
&msdn(netfx,type,System.Collections.Generic.Dictionary`2){System.Collections.Generic.Dictionaryクラス};は[[System.Collections.Hashtable>programming/netfx/collections/1_nongeneric_2_hashtable#Hashtable]]に相当するジェネリックコレクションです。 基本的な操作はHashtableと同じですが、キーに該当する要素がない場合の動作が異なります。 Hashtableではキーに該当する要素がない場合はnull(Nothing)が返されますが、DictionaryではKeyNotFoundExceptionがスローされます。 Hashtableではキーがないことを表すnullなのかキーに該当する値がnullなのか区別することが出来ませんが、これによりDictionaryではキーが設定されていない場合と値としてnullが設定されている場合を区別できるようになっています。
+

          
+
コレクション内の要素数を動的に増減できる点は[[List>programming/netfx/collections/2_generic_1_list]]と同じですが、Dictionaryは常にキーと値の対(''ペア'')を格納します。 格納した値にアクセスする場合は、インデックスの代わりにキーを指定します。 このコレクションではキー(名前)から目的の値を引くことができるため、Dictionary(辞書)という名前が付いています。 Dictionaryでは、任意の型(厳密にはGetHashCodeメソッドが一意なハッシュ値を返すオブジェクト)をキーとして指定することができます。
+

          
+
Dictionaryクラスは[[System.Collections.Hashtable>programming/netfx/collections/1_nongeneric_2_hashtable#Hashtable]]に相当するジェネリックコレクションですが、ただ単にジェネリックなHashtableというわけではなく、Hashtableと比べるとより高度な操作が可能になっています。
+

          
+
*基本操作
+
早速、Dictionaryクラスを使った基本的な操作について見ていきます。
+

          
+
**Dictionaryの作成・要素の取得と設定
+
Dictionaryを使う場合には、まずキーの型と値の型を決める必要があります。 Dictionaryを作成する際には、キー・値の順で型パラメータを指定します。 例えば、キーの型がstring/String、値の型がint/IntegerのDictionaryであれば、
+

          
+
#tabpage(C#)
+
#code(cs){{
+
Dictionary<string, int> dict;
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Dim dict As Dictionary(Of String, Integer)
+
}}
+
#tabpage-end
+

          
+
となります。
+

          
+
Dictionaryを使う場合には、最初に格納する要素を指定することも、空のDictionaryを作成しておいて後から要素を追加することも出来ます。 以下の例では空のDictionaryを作成して使うことにします。
+

          
+
Dictionaryに格納されている値を取得(参照)するには、インデックスを指定するかわりに配列と同様の記法でキーを指定します。 そうすることにより、Dictionaryに格納されているペアの中からキーに対応する値を取得することが出来ます。 また、取得だけでなく、キーに対応する特定の要素を設定(値の上書き)をすることも出来ます。 (C#ではインデクサによって、VB.NETでは既定のプロパティItemによって、配列と同様のインデックスによる取得・設定ができるようになっています。)
+

          
+
なお、現在Dictionary内に含まれている要素の数を取得するにはLengthプロパティではなく&msdn(netfx,member,System.Collections.Generic.Dictionary`2.Count){Countプロパティ};を参照します。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample
+
{
+
  static void Main()
+
  {
+
    // キーの型にstring、値の型にintを使用するDictionaryを作成
+
    Dictionary<string, int> dict = new Dictionary<string, int>();
+

          
+
    dict["foo"] = 16; // キー"foo"に対応する値として16を設定
+
    dict["bar"] = 72; // キー"bar"に対応する値として72を設定
+
    dict["baz"] = 42; // キー"baz"に対応する値として42を設定
+

          
+
    // 格納されている要素(ペア)の数を取得
+
    Console.WriteLine("Count = {0}", dict.Count);
+

          
+
    // キー"foo"に対応する値を取得
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+

          
+
    // キー"bar"に対応する値を取得
+
    Console.WriteLine("bar = {0}", dict["bar"]);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' キーの型にString、値の型にIntegerを使用するDictionaryを作成
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16 ' キー"foo"に対応する値として16を設定
+
    dict("bar") = 72 ' キー"bar"に対応する値として72を設定
+
    dict("baz") = 42 ' キー"baz"に対応する値として42を設定
+

          
+
    ' 格納されている要素(ペア)の数を取得
+
    Console.WriteLine("Count = {0}", dict.Count)
+

          
+
    ' キー"foo"に対応する値を取得
+
    Console.WriteLine("foo = {0}", dict("foo"))
+

          
+
    ' キー"bar"に対応する値を取得
+
    Console.WriteLine("bar = {0}", dict("bar"))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Count = 3
+
foo = 16
+
bar = 72
+
}}
+

          
+
Dictionaryに対して存在しないキーを指定した場合は例外&msdn(netfx,type,System.Collections.Generic.KeyNotFoundException){KeyNotFoundException};がスローされます。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+

          
+
    // 存在するキーに対応する値を取得
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+
    Console.WriteLine("bar = {0}", dict["bar"]);
+

          
+
    // 存在しないキー"hoge"に対応する値を取得
+
    Console.WriteLine("hoge = {0}", dict["hoge"]);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+
    dict("bar") = 72
+

          
+
    ' 存在するキーに対応する値を取得
+
    Console.WriteLine("foo = {0}", dict("foo"))
+
    Console.WriteLine("bar = {0}", dict("bar"))
+

          
+
    ' 存在しないキー"hoge"に対応する値を取得
+
    Console.WriteLine("hoge = {0}", dict("hoge"))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo = 16
+
bar = 72
+

          
+
ハンドルされていない例外: System.Collections.Generic.KeyNotFoundException: 指定されたキーはディレクトリ内に存在しませんでした。
+
   場所 System.Collections.Generic.Dictionary`2.get_Item(TKey key)
+
   場所 Sample.Main()
+
}}
+

          
+
事前にキーが存在するかどうかを確かめるには[[ContainsKeyメソッド>#ContainsKeyContainsValue]]を使うことが出来ます。
+

          
+
**要素の追加
+
既に例に挙げた通りインデクサでキーを指定することでDictionaryに要素を設定(追加)することができますが、これとは別に&msdn(netfx,member,System.Collections.Generic.Dictionary`2.Add){Addメソッド};を使うことでもDictionaryに要素を追加することが出来ます。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample
+
{
+
  static void Main()
+
  {
+
    // キーの型にstring、値の型にintを使用するDictionaryを作成
+
    Dictionary<string, int> dict = new Dictionary<string, int>();
+

          
+
    // Addメソッドを使って要素を追加
+
    dict.Add("foo", 16);
+
    dict.Add("bar", 72);
+
    dict.Add("baz", 42);
+

          
+
    // キーに対応する値を取得
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+
    Console.WriteLine("bar = {0}", dict["bar"]);
+
    Console.WriteLine("baz = {0}", dict["baz"]);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    ' Addメソッドを使って要素を追加
+
    dict.Add("foo", 16)
+
    dict.Add("bar", 72)
+
    dict.Add("baz", 42)
+

          
+
    ' キーに対応する値を取得
+
    Console.WriteLine("foo = {0}", dict("foo"))
+
    Console.WriteLine("bar = {0}", dict("bar"))
+
    Console.WriteLine("baz = {0}", dict("baz"))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo = 16
+
bar = 72
+
baz = 42
+
}}
+

          
+
一見するとインデクサを使う場合とAddメソッドを使う場合ではどちらも変わらないように見えますが、既にキーが存在する場合の動作が異なります。 インデクサでは既にキーが存在している場合は''値が上書きされる''のに対し、Addメソッドでは既にキーが存在している場合は''例外&msdn(netfx,type,System.ArgumentException){ArgumentException};がスローされます''。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+

          
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+

          
+
    // 既に存在するキーを指定して値を上書き
+
    dict["foo"] = 72;
+

          
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+

          
+
    // 既に存在するキーを指定して値を追加
+
    dict.Add("foo", 42);
+

          
+
    Console.WriteLine("foo = {0}", dict["foo"]);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+

          
+
    Console.WriteLine("foo = {0}", dict("foo"))
+

          
+
    ' 既に存在するキーを指定して値を上書き
+
    dict("foo") = 72
+

          
+
    Console.WriteLine("foo = {0}", dict("foo"))
+

          
+
    ' 既に存在するキーを指定して値を追加
+
    dict.Add("foo", 42)
+

          
+
    Console.WriteLine("foo = {0}", dict("foo"))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo = 16
+
foo = 72
+

          
+
ハンドルされていない例外: System.ArgumentException: 同一のキーを含む項目が既に追加されています。
+
   場所 System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
+
   場所 Sample.Main()
+
}}
+

          
+
このように、インデクサでは指定されたキーが存在しているかどうかに関わらず値が上書きされて設定されるのに対し、Addメソッドではキーが既に存在しているかを検査した上で値の追加が行われます。
+

          
+
**要素の削除
+
Dictionaryから要素を削除する場合は、&msdn(netfx,member,System.Collections.Generic.Dictionary`2.Remove){Removeメソッド};を使います。 このメソッドでは、指定されたキーを持つ要素をDictionaryから削除し、戻り値として実際に削除に成功したかどうかが返さます。 Addメソッドとは異なりRemoveメソッドに指定したキーを持つ要素が存在しない場合でも例外はスローされません(戻り値としてfalseが返されます)。
 

        

        
 
#tabpage(C#)
#tabpage(C#)
 
#code(cs){{
#code(cs){{
297,169 24,26
 
  {
  {
 
    Dictionary<string, int> dict = new Dictionary<string, int>();
    Dictionary<string, int> dict = new Dictionary<string, int>();
 

        

        
~
    dict["foo"] = 16;
    dict.Add("foo", 16); // キーを"foo"として値16を追加
+
    dict["bar"] = 72;
+

          
+
    Console.WriteLine("Count = {0}", dict.Count);
+

          
+
    // 存在するキー"bar"の要素を削除
+
    bool ret;
+

          
+
    ret = dict.Remove("bar");
+

          
+
    Console.WriteLine("キー'bar'の要素を削除" + (ret ? "しました" : "できませんでした"));
 

        

        
~
    Console.WriteLine("Count = {0}", dict.Count);
    dict["bar"] = 72; // キー"bar"の値として値72を設定
~

          
    dict["baz"] = 42; // キー"baz"の値として値42を設定
+
    // 存在しないキー"baz"の要素を削除
+
    ret = dict.Remove("baz");
+

          
+
    Console.WriteLine("キー'baz'の要素を削除" + (ret ? "しました" : "できませんでした"));
+

          
+
    Console.WriteLine("Count = {0}", dict.Count);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+
    dict("bar") = 72
+

          
+
    Console.WriteLine("Count = {0}", dict.Count)
+

          
+
    ' 存在するキー"bar"の要素を削除
+
    Dim ret As Boolean
+

          
+
    ret = dict.Remove("bar")
+

          
+
    Console.WriteLine("キー'bar'の要素を削除" + If(ret, "しました", "できませんでした"))
+

          
+
    Console.WriteLine("Count = {0}", dict.Count)
+

          
+
    ' 存在しないキー"baz"の要素を削除
+
    ret = dict.Remove("baz")
+

          
+
    Console.WriteLine("キー'baz'の要素を削除" + If(ret, "しました", "できませんでした"))
+

          
+
    Console.WriteLine("Count = {0}", dict.Count)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Count = 2
+
キー'bar'の要素を削除しました
+
Count = 1
+
キー'baz'の要素を削除できませんでした
+
Count = 1
+
}}
 

        

        
~
なお、インデクサを使って値にnull/Nothingを設定しても要素自体を削除することができません。 この場合、指定されたキーに対応する値としてnull/Nothingが設定されるだけで、要素の数は変わりません。
    Console.WriteLine(dict["foo"]); // キー"foo"の値を取得
-
    Console.WriteLine(dict["bar"]); // キー"bar"の値を取得
-
    Console.WriteLine(dict["baz"]); // キー"baz"の値を取得
 

        

        
~
また、Dictionary内のすべての要素を削除するには&msdn(netfx,member,System.Collections.Generic.Dictionary`2.Clear){Clearメソッド};を使うことが出来ます。
    // キーに該当する要素がない場合は、KeyNotFoundExceptionがスローされる
~

          
    try
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+
    dict["baz"] = 42;
+

          
+
    Console.WriteLine("Count = {0}", dict.Count);
+

          
+
    // Dictionary内のすべての要素を削除
+
    dict.Clear();
+

          
+
    Console.WriteLine("Count = {0}", dict.Count);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+
    dict("bar") = 72
+
    dict("baz") = 42
+

          
+
    Console.WriteLine("Count = {0}", dict.Count)
+

          
+
    ' Dictionary内のすべての要素を削除
+
    dict.Clear()
+

          
+
    Console.WriteLine("Count = {0}", dict.Count)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Count = 3
+
Count = 0
+
}}
+

          
+
**要素の列挙 [#Dictionary_Enumeration]
+
List等と同様にDictionaryもforeach文によって要素を列挙することができますが、Dictionaryではキー・値のペアは&msdn(netfx,type,System.Collections.Generic.KeyValuePair`2){KeyValuePair構造体};として格納されているため、列挙する場合もKeyValuePair構造体の形で列挙されます。 列挙されるKeyValuePairの&msdn(netfx,member,System.Collections.Generic.KeyValuePair`2.Key){Keyプロパティ};を参照することでキーを、&msdn(netfx,member,System.Collections.Generic.KeyValuePair`2.Value){Valueプロパティ};を参照することで値を取得することが出来ます。
+

          
+
また、キーと値を個別に列挙することも出来ます。 &msdn(netfx,member,System.Collections.Generic.Dictionary`2.Keys){Keysプロパティ};を参照することでDictionaryに格納されているすべてのキーを、&msdn(netfx,member,System.Collections.Generic.Dictionary`2.Values){Valuesプロパティ};を参照することですべての値を取得・列挙することが出来ます。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+
    dict["baz"] = 42;
+

          
+
    // Dictionaryに格納されているキーと値のペアを列挙
+
    foreach (KeyValuePair<string, int> pair in dict)
 
    {
    {
~
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
      Console.WriteLine(dict["hoge"]);
 
    }
    }
~

          
    catch (KeyNotFoundException)
+
    // Dictionaryに格納されているすべてのキーを列挙
+
    Console.WriteLine("[Keys]");
+

          
+
    foreach (string key in dict.Keys)
 
    {
    {
~
      Console.WriteLine(key);
      Console.WriteLine("KeyNotFoundException");
 
    }
    }
 

        

        
~
    // Dictionaryに格納されているすべての値を列挙
    Console.WriteLine(dict.Count); // 現在のキーと値の対の数を取得
+
    Console.WriteLine("[Values]");
+

          
+
    foreach (int val in dict.Values)
+
    {
+
      Console.WriteLine(val);
+
    }
 
  }
  }
 
}
}
 
}}
}}
470,183 54,46
 

        

        
 
Class Sample
Class Sample
 
  Shared Sub Main()
  Shared Sub Main()
~
    Dim dict As New Dictionary(Of String, Integer)()
    Dim dict As New Dictionary(Of String, Integer)
~

          

          
~
    dict("foo") = 16
    dict.Add("foo", 16) ' キーを"foo"として値16を追加
+
    dict("bar") = 72
+
    dict("baz") = 42
+

          
+
    ' Dictionaryに格納されているキーと値のペアを列挙
+
    For Each pair As KeyValuePair(Of String, Integer) In dict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
+
    Next
+

          
+
    ' Dictionaryに格納されているすべてのキーを列挙
+
    Console.WriteLine("[Keys]")
+

          
+
    For Each key As String In dict.Keys
+
      Console.WriteLine(key)
+
    Next
+

          
+
    ' Dictionaryに格納されているすべての値を列挙
+
    Console.WriteLine("[Values]")
+

          
+
    For Each val As Integer In dict.Values
+
      Console.WriteLine(val)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo => 16
+
bar => 72
+
baz => 42
+
[Keys]
+
foo
+
bar
+
baz
+
[Values]
+
16
+
72
+
42
+
}}
+

          
+
***要素の列挙と順序
+
Dictionaryは''順序を持たないコレクション''であるため、Dictionaryの要素を列挙する場合、List等とは異なり''必ずしも追加した順で列挙されるとは限らない''点に注意が必要です。 また、順序を持たないためインデックスを指定してキー・値を参照することも出来ないため、for文による列挙は行えません。 インデックスを使った操作を行いたい場合は、次の例のように一旦DictionaryからListに変換したり、キーと値を別の配列・コレクションにコピーしてから列挙する、といった方法を取る必要があります。
+

          
+
#tabpage(C#)
+
#code(cs,DictionaryからListに変換して列挙する){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+
    dict["baz"] = 42;
+

          
+
    // Dictionaryと同じ内容を含むListを作成
+
    List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(dict);
+

          
+
    // for文を使って作成したListを列挙
+
    for (int i = 0; i < list.Count; i++)
+
    {
+
      Console.WriteLine("{0} : {1} => {2}", i, list[i].Key, list[i].Value);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb,DictionaryからListに変換して列挙する){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+
    dict("bar") = 72
+
    dict("baz") = 42
+

          
+
    ' Dictionaryと同じ内容を含むListを作成
+
    Dim list As New List(Of KeyValuePair(Of String, Integer))(dict)
+

          
+
    ' Forステートメントを使って作成したListを列挙
+
    For i As Integer = 0 To list.Count - 1
+
      Console.WriteLine("{0} : {1} => {2}", i, list(i).Key, list(i).Value)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

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

          
+
次の例では、CopyToメソッドを使って、キーと値を個別の配列にコピーしてから列挙しています。
+

          
+
#tabpage(C#)
+
#code(cs,Dictionaryからキーと値を別の配列にコピーして列挙する){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+
    dict["baz"] = 42;
+

          
+
    // キーと値を格納するための配列を作成
+
    string[] keys = new string[dict.Count];
+
    int[] values = new int[dict.Count];
+

          
+
    // キーと値を配列にコピー
+
    dict.Keys.CopyTo(keys, 0);
+
    dict.Values.CopyTo(values, 0);
+

          
+
    // for文を使ってコピーしたキーと値の配列を列挙
+
    for (int i = 0; i < keys.Length; i++)
+
    {
+
      Console.WriteLine("{0} : {1} => {2}", i, keys[i], values[i]);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb,Dictionaryからキーと値を別の配列にコピーして列挙する){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
 

        

        
~
    dict("foo") = 16
    dict("bar") = 72 ' キー"bar"の値として値72を設定
~
    dict("bar") = 72
    dict("baz") = 42 ' キー"baz"の値として値42を設定
+
    dict("baz") = 42
 

        

        
~
    ' キーと値を格納するための配列を作成
    Console.WriteLine(dict("foo")) ' キー"foo"の値を取得
~
    Dim keys(dict.Count - 1) As String
    Console.WriteLine(dict("bar")) ' キー"bar"の値を取得
~
    Dim values(dict.Count - 1) As Integer
    Console.WriteLine(dict("baz")) ' キー"baz"の値を取得
 

        

        
~
    ' キーと値を配列にコピー
    ' キーに該当する要素がない場合は、KeyNotFoundExceptionがスローされる
~
    dict.Keys.CopyTo(keys, 0)
    Try
~
    dict.Values.CopyTo(values, 0)
      Console.WriteLine(dict("hoge"))
-
    Catch ex As KeyNotFoundException
-
      Console.WriteLine("KeyNotFoundException")
-
    End Try
 

        

        
~
    ' Forステートメントを使ってコピーしたキーと値の配列を列挙
    Console.WriteLine(dict.Count) ' 現在のキーと値の対の数を取得
+
    For i As Integer = 0 To keys.Length - 1
+
      Console.WriteLine("{0} : {1} => {2}", i, keys(i), values(i))
+
    Next
 
  End Sub
  End Sub
 
End Class
End Class
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
0 : foo => 16
16
~
1 : bar => 72
72
~
2 : baz => 42
42
-
KeyNotFoundException
-
3
 
}}
}}
 

        

        
~
***列挙中の要素の追加・削除
**ソート
~
Dictionaryをforeach文で列挙している最中に要素の追加や削除などの変更を行おうとすると、例外&msdn(netfx,type,System.InvalidOperationException){InvalidOperationException};がスローされます。
Dictionaryクラスのソートについては[[programming/netfx/sorting/0_basictypes]]で詳しく解説しています。
-

          
-
**列挙操作
-
Hashtableと同様、Dictionaryもforeach文によって要素を列挙することができますが、個々のペアはDictionaryEntry構造体ではなく&msdn(netfx,type,System.Collections.Generic.KeyValuePair`2){KeyValuePair構造体};で格納されます。 列挙の際、必ずしも追加した順で列挙されるとは限らない点に注意が必要です。 また、すべてのキーはKeysプロパティ、値はValuesプロパティを通して参照できます。
 

        

        
 
#tabpage(C#)
#tabpage(C#)
~
#code(cs,列挙中の要素の削除){{
#code(cs){{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
 

        

        
660,16 107,32
 
    dict["bar"] = 72;
    dict["bar"] = 72;
 
    dict["baz"] = 42;
    dict["baz"] = 42;
 

        

        
~
    foreach (KeyValuePair<string, int> pair in dict)
    // Dictionaryに格納されているキーと値のペアを列挙
-
    foreach (KeyValuePair<string, int> entry in dict)
-
    {
-
      Console.WriteLine("{0} => {1}", entry.Key, entry.Value);
-
    }
-

          
-
    // Dictionaryに格納されているすべてのキーを列挙
-
    Console.WriteLine("[Keys]");
-

          
-
    foreach (string key in dict.Keys)
-
    {
-
      Console.WriteLine(key);
-
    }
-

          
-
    // Dictionaryに格納されているすべての値を列挙
-
    Console.WriteLine("[Values]");
-

          
-
    foreach (int val in dict.Values)
 
    {
    {
~
      // 値が42の場合は、削除する
      Console.WriteLine(val);
+
      if (pair.Value == 42) dict.Remove(pair.Key);
 
    }
    }
 
  }
  }
 
}
}
 
}}
}}
 
#tabpage(VB)
#tabpage(VB)
~
#code(vb,列挙中の要素の削除){{
#code(vb){{
 
Imports System
Imports System
 
Imports System.Collections.Generic
Imports System.Collections.Generic
 

        

        
681,9 144,23
 
    dict("bar") = 72
    dict("bar") = 72
 
    dict("baz") = 42
    dict("baz") = 42
 

        

        
~
    For Each pair As KeyValuePair(Of String, Integer) In dict
    ' Dictionaryに格納されているキーと値のペアを列挙
~
      ' 値が42の場合は、削除する
    For Each entry As KeyValuePair(Of String, Integer) In dict
~
      If pair.Value = 42 Then dict.Remove(pair.Key)
      Console.WriteLine("{0} => {1}", entry.Key, entry.Value)
-
    Next
-

          
-
    ' Dictionaryに格納されているすべてのキーを列挙
-
    Console.WriteLine("[Keys]")
-

          
-
    For Each key As String In dict.Keys
-
      Console.WriteLine(key)
-
    Next
-

          
-
    ' Dictionaryに格納されているすべての値を列挙
-
    Console.WriteLine("[Values]")
-

          
-
    For Each val As Integer In dict.Values
-
      Console.WriteLine(val)
 
    Next
    Next
 
  End Sub
  End Sub
 
End Class
End Class
691,17 168,23
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
ハンドルされていない例外: System.InvalidOperationException: コレクションが変更されました。列挙操作は実行されない可能性があります。
foo => 16
~
   場所 System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
bar => 72
~
   場所 Sample.Main()
baz => 42
-
[Keys]
-
foo
-
bar
-
baz
-
[Values]
-
16
-
72
-
42
 
}}
}}
 

        

        
~
例外が発生する理由や回避する方法については[[programming/netfx/enumeration]]でも解説していますが、Dictionaryクラスには[[List.ForEachメソッド>programming/netfx/collections/2_generic_1_list#List_Enumeartion]]のような列挙操作を行うメソッドは用意されていないためこれを使った方法は適用できません。
Dictionaryは順序を持たないコレクションであり、インデックスを指定してキー・値を参照することも出来ないため、for文による列挙は行えません。 インデックスを使った操作を行いたい場合は、次の例のように一旦DictionaryからListに変換したり、キーと値を別の配列・コレクションにコピーしてから列挙する、といった方法を取る必要があります。
+

          
+
上記の例のように条件に合う要素を検索して削除するといった場合は、列挙による要素の検索と削除を別々のforeach文で行うようにすることでInvalidOperationExceptionの発生を回避することが出来ます。 また別の方法として、次の例のように別のDictionaryを用意して、元のDictionaryから条件に合う要素だけを別のDictionaryコピーするという方法もあります。
 

        

        
 
#tabpage(C#)
#tabpage(C#)
~
#code(cs,列挙中の要素の削除){{
#code(cs,DictionaryからListに変換して列挙する){{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
 

        

        
715,25 198,18
 
    dict["bar"] = 72;
    dict["bar"] = 72;
 
    dict["baz"] = 42;
    dict["baz"] = 42;
 

        

        
~
    Dictionary<string, int> newdict = new Dictionary<string, int>();
    // Dictionaryと同じ内容を含むListを作成
-
    List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(dict);
 

        

        
~
    // 値が42の要素を除いて新しいDictionaryに内容をコピーする
    // for文を使って作成したListを列挙
~
    foreach (KeyValuePair<string, int> pair in dict)
    for (int i = 0; i < list.Count; i++) {
~
    {
      Console.WriteLine("{0} : {1} => {2}", i, list[i].Key, list[i].Value);
+
      // 値が42以外の場合のみ、新しいDictionaryに格納する
+
      if (pair.Value != 42) newdict[pair.Key] = pair.Value;
+
    }
+

          
+
    // 新しいDictionaryの内容を列挙
+
    foreach (KeyValuePair<string, int> pair in newdict)
+
    {
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
 
    }
    }
 
  }
  }
 
}
}
 
}}
}}
 
#tabpage(VB)
#tabpage(VB)
~
#code(vb,列挙中の要素の削除){{
#code(vb,DictionaryからListに変換して列挙する){{
 
Imports System
Imports System
 
Imports System.Collections.Generic
Imports System.Collections.Generic
 

        

        
745,16 221,12
 
    dict("bar") = 72
    dict("bar") = 72
 
    dict("baz") = 42
    dict("baz") = 42
 

        

        
~
    Dim newdict As New Dictionary(Of String, Integer)()
    ' Dictionaryと同じ内容を含むListを作成
-
    Dim list As New List(Of KeyValuePair(Of String, Integer))(dict)
 

        

        
~
    ' 値が42の要素を除いて新しいDictionaryに内容をコピーする
    ' Forステートメントを使って作成したListを列挙
~
    For Each pair As KeyValuePair(Of String, Integer) In dict
    For i As Integer = 0 To list.Count - 1
~
      If pair.Value <> 42 Then newdict(pair.Key) = pair.Value
      Console.WriteLine("{0} : {1} => {2}", i, list(i).Key, list(i).Value)
+
    Next
+

          
+
    ' 新しいDictionaryの内容を列挙
+
    For Each pair As KeyValuePair(Of String, Integer) In newdict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
 
    Next
    Next
 
  End Sub
  End Sub
 
End Class
End Class
762,22 234,13
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
foo => 16
0 : foo => 16
~
bar => 72
1 : bar => 72
-
2 : baz => 42
 
}}
}}
 

        

        
+

          
+
**要素の並べ替え (ソート)
+
Dictionaryは順序を持たないことからDictionaryをソートされた状態にすることは出来ず、またそのようなメソッドも用意されていません。 ソートされた状態で内容を取得したい場合は、[[要素の列挙>#Dictionary_Enumeration]]に挙げたような方法で一旦Listに変換するか、値とキーをコピーしてからソートするなどの方法をとる必要があります。 Dictionaryとソートについてより詳しくは[[programming/netfx/sorting/0_basictypes#SortDictionary]]で解説しています。
+

          
+
また、常に要素を並べ替えた状態にしておきたい場合や格納される要素の順序が重要になる場合は、Dictionaryの代わりに[[SortedListやSortedDictionary>programming/netfx/collections/2_generic_3_sortedlist_sorteddictionary]]を使うことが出来ます。
+

          
+

          
+
**要素の検索 [#ContainsKeyContainsValue]
+
Dictionaryに指定したキーが存在するかどうかを調べるには&msdn(netfx,member,System.Collections.Generic.Dictionary`2.ContainsKey){ContainsKeyメソッド};、値が存在するかどうかを調べるには&msdn(netfx,member,System.Collections.Generic.Dictionary`2.ContainsValue){ContainsValueメソッド};を使います。
+

          
 
#tabpage(C#)
#tabpage(C#)
~
#code(cs,要素の検索){{
#code(cs,Dictionaryからキーと値を別の配列にコピーして列挙する){{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
 

        

        
791,22 254,23
 
    dict["bar"] = 72;
    dict["bar"] = 72;
 
    dict["baz"] = 42;
    dict["baz"] = 42;
 

        

        
~
    // キー"foo"を持つ要素があるかどうか
    // キーと値を格納するための配列を作成
~
    Console.WriteLine("ContainsKey(foo) = {0}", dict.ContainsKey("foo"));
    string[] keys = new string[dict.Count];
-
    int[] values = new int[dict.Count];
 

        

        
~
    // キー"hoge"を持つ要素があるかどうか
    // キーと値を配列にコピー
~
    Console.WriteLine("ContainsKey(hoge) = {0}", dict.ContainsKey("hoge"));
    dict.Keys.CopyTo(keys, 0);
-
    dict.Values.CopyTo(values, 0);
 

        

        
~
    // 値16を持つ要素があるかどうか
    // for文を使ってコピーしたキーと値の配列を列挙
~
    Console.WriteLine("ContainsValue(16) = {0}", dict.ContainsValue(16));
    for (int i = 0; i < keys.Length; i++) {
~

          
      Console.WriteLine("{0} : {1} => {2}", i, keys[i], values[i]);
~
    // 値9を持つ要素があるかどうか
    }
+
    Console.WriteLine("ContainsValue(9) = {0}", dict.ContainsValue(9));
 
  }
  }
 
}
}
 
}}
}}
 
#tabpage(VB)
#tabpage(VB)
~
#code(vb,要素の検索){{
#code(vb,Dictionaryからキーと値を別の配列にコピーして列挙する){{
 
Imports System
Imports System
 
Imports System.Collections.Generic
Imports System.Collections.Generic
 

        

        
818,38 282,42
 
    dict("bar") = 72
    dict("bar") = 72
 
    dict("baz") = 42
    dict("baz") = 42
 

        

        
~
    ' キー"foo"を持つ要素があるかどうか
    ' キーと値を格納するための配列を作成
~
    Console.WriteLine("ContainsKey(foo) = {0}", dict.ContainsKey("foo"))
    Dim keys(dict.Count - 1) As String
-
    Dim values(dict.Count - 1) As Integer
 

        

        
~
    ' キー"hoge"を持つ要素があるかどうか
    ' キーと値を配列にコピー
~
    Console.WriteLine("ContainsKey(hoge) = {0}", dict.ContainsKey("hoge"))
    dict.Keys.CopyTo(keys, 0)
-
    dict.Values.CopyTo(values, 0)
 

        

        
~
    ' 値16を持つ要素があるかどうか
    ' Forステートメントを使ってコピーしたキーと値の配列を列挙
~
    Console.WriteLine("ContainsValue(16) = {0}", dict.ContainsValue(16))
    For i As Integer = 0 To keys.Length - 1
~

          
      Console.WriteLine("{0} : {1} => {2}", i, keys(i), values(i))
~
    ' 値9を持つ要素があるかどうか
    Next
+
    Console.WriteLine("ContainsValue(9) = {0}", dict.ContainsValue(9))
 
  End Sub
  End Sub
 
End Class
End Class
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
ContainsKey(foo) = True
0 : foo => 16
~
ContainsKey(hoge) = False
1 : bar => 72
~
ContainsValue(16) = True
2 : baz => 42
+
ContainsValue(9) = False
 
}}
}}
 

        

        
-
Dictionaryは順序を持たないことからソートされた状態にすることも出来ず、そのようなメソッドも用意されていません。 ソートされた状態で内容を取得したい場合も、一旦Listに変換するか、値とキーをコピーしてからソートするなどの方法をとる必要があります。 Dictionaryとソートについては[[programming/netfx/sorting/0_basictypes#SortDictionary]]で解説しています。
-

          
-
なお、Dictionaryクラスには、List.ForEachメソッドのような列挙操作を行うメソッドは用意されていません。
 

        

        
~
***キーの有無チェックと値の取得 (TryGetValue) [#TryGetValue]
**キーの有無チェックと値の取得
~
Dictionaryではキーが存在しない場合は例外KeyNotFoundExceptionがスローされるため、事前にキーがあるかどうか調べるなどする必要があります。 しかし、&msdn(netfx,member,System.Collections.Generic.Dictionary`2.TryGetValue){TryGetValueメソッド};を使うとキーの有無チェックと値の取得を同時に行えるため、キーの有無チェックやKeyNotFoundExceptionのキャッチをする必要が無くなります。
Hashtableではキーに該当する要素がない場合はnull(Nothing)が返されるので、とりあえず取得してnullと比較するという方法が取れますが、Dictionaryではキーがない場合は例外がスローされるため事前にキーがあるかどうか調べるなどする必要があります。
 

        

        
~
このメソッドは、ContainsKeyメソッドと値の取得を組み合わせた動作をするメソッドと言えます。 キーの有無はTryGetValueメソッドの戻り値で取得でき、該当するキーが存在する場合はキーに対応する値がoutパラメータに格納されます。
&msdn(netfx,member,System.Collections.Generic.Dictionary`2.TryGetValue){TryGetValueメソッド};を使うと、キーの有無チェックやKeyNotFoundExceptionのキャッチをする必要が無くなります。 キーの有無チェックと値の取得を同時に行えます。 このメソッドは、ContainsKeyメソッドと値の取得を組み合わせた動作をするメソッドと言えます。 キーの有無はTryGetValueメソッドの戻り値で取得でき、キーがある場合は該当する値がoutパラメータに格納されます。
 

        

        
 
#tabpage(C#)
#tabpage(C#)
 
#code(cs){{
#code(cs){{
 
using System;
using System;
-
using System.Collections;
 
using System.Collections.Generic;
using System.Collections.Generic;
 

        

        
 
class Sample
class Sample
861,25 329,27
 
    dict["foo"] = 16;
    dict["foo"] = 16;
 
    dict["bar"] = 72;
    dict["bar"] = 72;
 

        

        
~
    // Dictionaryからキー"foo"に対応する値を取得
    Hashtable hash = new Hashtable();
+
    int val;
 

        

        
~
    if (dict.TryGetValue("foo", out val))
    hash["foo"] = 16;
-
    hash["bar"] = 72;
-

          
-
    // Dictionaryからキー"foo"に該当する値を取得
-
    int valDict;
-

          
-
    if (dict.TryGetValue("foo", out valDict))
 
    {
    {
 
      // 取得できたら表示
      // 取得できたら表示
~
      Console.WriteLine("foo => {0}", val);
      Console.WriteLine("foo => {0}", valDict);
 
    }
    }
 

        

        
~
    // Dictionaryからキー"hoge"に対応する値を取得
    // Hashtableからキー"foo"に該当する値を取得
~
    if (dict.TryGetValue("hoge", out val))
    object valHash = hash["foo"];
~
    {

          
~
      // 取得できたら表示
    if (valHash != null)
+
      Console.WriteLine("hoge => {0}", val);
+
    }
+
    else
 
    {
    {
~
      // 存在するキーが無い場合
      // 取得出来たらintにキャストして表示
~
      Console.WriteLine("key 'hoge' not found");
      Console.WriteLine("foo => {0}", (int)valHash);
 
    }
    }
 
  }
  }
 
}
}
887,6 357,7
 
#tabpage(VB)
#tabpage(VB)
 
#code(vb){{
#code(vb){{
 
Imports System
Imports System
-
Imports System.Collections
 
Imports System.Collections.Generic
Imports System.Collections.Generic
 

        

        
 
Class Sample
Class Sample
896,21 367,25
 
    dict("foo") = 16
    dict("foo") = 16
 
    dict("bar") = 72
    dict("bar") = 72
 

        

        
~
    ' Dictionaryからキー"foo"に対応する値を取得
    Dim hash As New Hashtable()
~
    Dim val As Integer

          
-
    hash("foo") = 16
-
    hash("bar") = 72
-

          
-
    ' Dictionaryからキー"foo"に該当する値を取得
-
    Dim valDict As Integer
 

        

        
~
    If dict.TryGetValue("foo", val) Then
    If dict.TryGetValue("foo", valDict) Then
 
      ' 取得できたら表示
      ' 取得できたら表示
~
      Console.WriteLine("foo => {0}", val)
      Console.WriteLine("foo => {0}", valDict)
 
    End If
    End If
 

        

        
~
    ' Dictionaryからキー"hoge"に対応する値を取得
    ' Hashtableからキー"foo"に該当する値を取得
~
    If dict.TryGetValue("hoge", val) Then
    Dim valHash As Object = hash("foo")
~
      ' 取得できたら表示

          
~
      Console.WriteLine("hoge => {0}", val)
    If Not valHash Is Nothing Then
~
    Else
      ' 取得出来たらIntegerにキャストして表示
~
      ' 存在するキーが無い場合
      Console.WriteLine("foo => {0}", CInt(valHash))
+
      Console.WriteLine("key 'hoge' not found")
 
    End If
    End If
 
  End Sub
  End Sub
 
End Class
End Class
919,12 394,9
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
 
foo => 16
foo => 16
~
key 'hoge' not found
foo => 16
 
}}
}}
 

        

        
+

          
+
*Dictionaryとキー
+

          
 
**キー比較のカスタマイズ
**キー比較のカスタマイズ
 
Hashtableと同様、コンストラクタで適切なIEqualityComparer<T>インターフェイスを指定することで、Dictionaryのキー比較時の動作をカスタマイズ出来ます。 例えば、文字列をキーとした場合に大文字小文字を無視するようにするといったことが出来ます。 以下は、StringComparerクラスを使って、大文字小文字を無視するDictionaryを作成する例です。
Hashtableと同様、コンストラクタで適切なIEqualityComparer<T>インターフェイスを指定することで、Dictionaryのキー比較時の動作をカスタマイズ出来ます。 例えば、文字列をキーとした場合に大文字小文字を無視するようにするといったことが出来ます。 以下は、StringComparerクラスを使って、大文字小文字を無視するDictionaryを作成する例です。
 

        

        
1026,7 498,7
 
**キーと複合型
**キーと複合型
 
Dictionaryクラスでは、キーとなる型が&msdn(netfx,member,System.Object.Equals){Equalsメソッド};と&msdn(netfx,member,System.Object.GetHashCode){GetHashCodeメソッド};をオーバーライドしていて、かつ適切な値を返すように実装されていないと正しく動作しません。 キーに独自に定義した型を指定してDictionaryを使用したい場合は、これらのメソッドをオーバーライドして適切に実装するか、Dictionaryのコンストラクタに&msdn(netfx,type,System.Collections.Generic.IEqualityComparer`1){IEqualityComparer<T>インターフェイス};を指定する必要があります。 IEqualityComparer<T>インターフェイスの実装方法や、独自に定義した型をキーにする例については[[programming/netfx/comparison/1_equation]]で解説しています。
Dictionaryクラスでは、キーとなる型が&msdn(netfx,member,System.Object.Equals){Equalsメソッド};と&msdn(netfx,member,System.Object.GetHashCode){GetHashCodeメソッド};をオーバーライドしていて、かつ適切な値を返すように実装されていないと正しく動作しません。 キーに独自に定義した型を指定してDictionaryを使用したい場合は、これらのメソッドをオーバーライドして適切に実装するか、Dictionaryのコンストラクタに&msdn(netfx,type,System.Collections.Generic.IEqualityComparer`1){IEqualityComparer<T>インターフェイス};を指定する必要があります。 IEqualityComparer<T>インターフェイスの実装方法や、独自に定義した型をキーにする例については[[programming/netfx/comparison/1_equation]]で解説しています。
 

        

        
~
*入れ子
**入れ子
 
Listや他のジェネリックコレクション同様、Dictionaryもキー・値ともに任意の型に型付けすることができるため、キー・値に他のジェネリックコレクション型を格納するようにすることも出来ます。 次の例では、Dictionaryクラスを使って文字列の辞書を処理するために入れ子になったDictionary<char, List<string>>を作成し、要素の追加・削除・列挙などの操作を行っています。
Listや他のジェネリックコレクション同様、Dictionaryもキー・値ともに任意の型に型付けすることができるため、キー・値に他のジェネリックコレクション型を格納するようにすることも出来ます。 次の例では、Dictionaryクラスを使って文字列の辞書を処理するために入れ子になったDictionary<char, List<string>>を作成し、要素の追加・削除・列挙などの操作を行っています。
 

        

        
 
#tabpage(C#)
#tabpage(C#)
1216,301 688,5
 
}}
}}
 

        

        
 

        

        
+
*読み取り専用 [#Dictionary_ReadOnly]
+
Dictionaryには[[List.AsReadOnlyメソッド>programming/netfx/collections/2_generic_1_list#List.AsReadOnly]]のようなメソッドが存在しないため、読み取り専用のDictionaryを作成する手段が存在しません。 また、[[ReadOnlyCollection>programming/netfx/collections/3_objectmodel_1_collection#ReadOnlyCollection]]に相当するようなクラスも存在していないため読み取り専用のDictionaryが必要な場合は、自前で実装する必要があります。
+

          
+
次の例はそのような読み取り専用のクラスReadOnlyDictionaryを実装した例です。 動作やスローする例外等は[[ReadOnlyCollection>programming/netfx/collections/3_objectmodel_1_collection#ReadOnlyCollection]]に近いものとなるようにしています。 また、IDictionaryに対して拡張メソッドAsReadOnlyを追加するためのクラスIDictionaryExtensionsも作成しています。 なお、この例で実装しているReadOnlyDictionaryクラスでは元のDictionaryを読み取り専用にする訳ではないので注意してください。 元になったDictionaryは、依然として要素の追加・削除・変更が可能です。
+

          
+
#tabpage(C#)
+
#code(cs,ReadOnlyDictionaryの実装例){{
+
using System;
+
using System.Collections.Generic;
+

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

          
+
    dict["foo"] = 16;
+
    dict["bar"] = 72;
+

          
+
    // 読み取り専用のIDictionaryを作成する
+
    IDictionary<string, int> readOnlyDict = dict.AsReadOnly();
+

          
+
    foreach (KeyValuePair<string, int> pair in readOnlyDict)
+
    {
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
+
    }
+

          
+
    // 既に存在するキーの値の変更を試みる
+
    readOnlyDict["foo"] = 42;
+
  }
+
}
+

          
+
// IDictionaryに拡張メソッドAsReadOnlyを追加するためのクラス
+
public static class IDictionaryExtensions {
+
  public static IDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
+
  {
+
    if (dictionary == null)
+
      throw new ArgumentNullException("dictionary");
+
    else if (dictionary.IsReadOnly)
+
      return dictionary;
+
    else
+
      return new ReadOnlyDictionary<TKey, TValue>(dictionary);
+
  }
+
}
+

          
+
// IDictionaryをラップして読み取りアクセスのみ許可するようにIDictionaryを実装したクラス
+
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
+
  // ラップするIDictionary
+
  private IDictionary<TKey, TValue> dictionary;
+

          
+
  public TValue this[TKey key] {
+
    get { return dictionary[key]; }
+
    set { throw ReadOnlyException(); }
+
  }
+

          
+
  public ICollection<TKey> Keys {
+
    get { return dictionary.Keys; }
+
  }
+

          
+
  public ICollection<TValue> Values {
+
    get { return dictionary.Values; }
+
  }
+

          
+
  public int Count {
+
    get { return dictionary.Count; }
+
  }
+

          
+
  public bool IsReadOnly {
+
    get { return true; }
+
  }
+

          
+
  public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
+
  {
+
    if (dictionary == null)
+
      throw new ArgumentNullException("dictionary");
+

          
+
    this.dictionary = dictionary;
+
  }
+

          
+
  /*
+
   * 元のDictionaryに対する読み取りのみが行われる操作
+
   */
+
  public bool Contains(KeyValuePair<TKey, TValue> item)
+
  {
+
    return dictionary.Contains(item);
+
  }
+

          
+
  public bool ContainsKey(TKey key)
+
  {
+
    return dictionary.ContainsKey(key);
+
  }
+

          
+
  public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+
  {
+
    dictionary.CopyTo(array, arrayIndex);
+
  }
+

          
+
  public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+
  {
+
    return dictionary.GetEnumerator();
+
  }
+

          
+
  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+
  {
+
    return dictionary.GetEnumerator();
+
  }
+

          
+
  public bool TryGetValue(TKey key, out TValue @value)
+
  {
+
    return dictionary.TryGetValue(key, out @value);
+
  }
+

          
+
  /*
+
   * 元のDictionaryに対する変更が行われる操作
+
   */
+
  public void Add(KeyValuePair<TKey, TValue> item)
+
  {
+
    throw ReadOnlyException();
+
  }
+

          
+
  public void Add(TKey key, TValue @value)
+
  {
+
    throw ReadOnlyException();
+
  }
+

          
+
  public void Clear()
+
  {
+
    throw ReadOnlyException();
+
  }
+

          
+
  public bool Remove(KeyValuePair<TKey, TValue> item)
+
  {
+
    throw ReadOnlyException();
+
  }
+

          
+
  public bool Remove(TKey key)
+
  {
+
    throw ReadOnlyException();
+
  }
+

          
+
  private Exception ReadOnlyException()
+
  {
+
    return new NotSupportedException("コレクションは読み取り専用です");
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb,ReadOnlyDictionaryの実装例){{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim dict As New Dictionary(Of String, Integer)()
+

          
+
    dict("foo") = 16
+
    dict("bar") = 72
+

          
+
    ' 読み取り専用のIDictionaryを作成する
+
    Dim readOnlyDict As IDictionary(Of String, Integer) = dict.AsReadOnly()
+

          
+
    For Each pair As KeyValuePair(Of String, Integer) In readOnlyDict
+
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
+
    Next
+

          
+
    ' 既に存在するキーの値の変更を試みる
+
    readOnlyDict("foo") = 42
+
  End Sub
+
End Class
+

          
+
' IDictionaryに拡張メソッドAsReadOnlyを追加するためのモジュール
+
Public Module IDictionaryExtensions
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function AsReadOnly(Of TKey, TValue)(ByVal dictionary As IDictionary(Of TKey, TValue)) As IDictionary(Of TKey, TValue)
+
    If dictionary Is Nothing Then
+
      Throw New ArgumentNullException("dictionary")
+
    Else If dictionary.IsReadOnly Then
+
      Return dictionary
+
    Else
+
      Return New ReadOnlyDictionary(Of TKey, TValue)(dictionary)
+
    End If
+
  End Function
+
End Module
+

          
+
' IDictionaryをラップして読み取りアクセスのみ許可するようにIDictionaryを実装したクラス
+
Public Class ReadOnlyDictionary(Of TKey, TValue)
+
  Implements IDictionary(Of TKey, TValue)
+

          
+
  ' ラップするIDictionary
+
  Private dictionary As IDictionary(Of TKey, TValue)
+

          
+
  Public Default Property Item(ByVal key As TKey) As TValue Implements IDictionary(Of TKey, TValue).Item
+
    Get
+
      Return dictionary(key)
+
    End Get
+
    Set (ByVal val As TValue)
+
      Throw ReadOnlyException()
+
    End Set
+
  End Property
+

          
+
  Public ReadOnly Property Keys As ICollection(Of TKey) Implements IDictionary(Of TKey, TValue).Keys
+
    Get
+
      Return dictionary.Keys
+
    End Get
+
  End Property
+

          
+
  Public ReadOnly Property Values As ICollection(Of TValue) Implements IDictionary(Of TKey, TValue).Values
+
    Get
+
      Return dictionary.Values
+
    End Get
+
  End Property
+

          
+
  Public ReadOnly Property Count As Integer Implements IDictionary(Of TKey, TValue).Count
+
    Get
+
      Return dictionary.Count
+
    End Get
+
  End Property
+

          
+
  Public ReadOnly Property IsReadOnly As Boolean Implements IDictionary(Of TKey, TValue).IsReadOnly
+
    Get
+
      Return True
+
    End Get
+
  End Property
+

          
+
  Public Sub New(ByVal dictionary As IDictionary(Of TKey, TValue))
+
    If dictionary Is Nothing Then Throw New ArgumentNullException("dictionary")
+

          
+
    Me.dictionary = dictionary
+
  End Sub
+

          
+
  '
+
  ' 元のDictionaryに対する読み取りのみが行われる操作
+
  '
+
  Public Function Contains(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements IDictionary(Of TKey, TValue).Contains
+
    Return dictionary.Contains(item)
+
  End Function
+

          
+
  Public Function ContainsKey(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).ContainsKey
+
    Return dictionary.ContainsKey(key)
+
  End Function
+

          
+
  Public Sub CopyTo(ByVal array() As KeyValuePair(Of TKey, TValue), ByVal arrayIndex As Integer) Implements IDictionary(Of TKey, TValue).CopyTo
+
    dictionary.CopyTo(array, arrayIndex)
+
  End Sub
+

          
+
  Public Function GetEnumerator() As IEnumerator(Of KeyValuePair(Of TKey, TValue)) Implements IEnumerable(Of KeyValuePair(Of TKey, TValue)).GetEnumerator
+
    Return dictionary.GetEnumerator()
+
  End Function
+

          
+
  Private Function GetNonGenericEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
+
    Return dictionary.GetEnumerator()
+
  End Function
+

          
+
  Public Function TryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean Implements IDictionary(Of TKey, TValue).TryGetValue
+
    Return dictionary.TryGetValue(key, value)
+
  End Function
+

          
+
  '
+
  ' 元のDictionaryに対する変更が行われる操作
+
  '
+
  Public Sub Add(ByVal item As KeyValuePair(Of TKey, TValue)) Implements IDictionary(Of TKey, TValue).Add
+
    Throw ReadOnlyException()
+
  End Sub
+

          
+
  Public Sub Add(ByVal key As TKey, ByVal value As TValue) Implements IDictionary(Of TKey, TValue).Add
+
    Throw ReadOnlyException()
+
  End Sub
+

          
+
  Public Sub Clear() Implements IDictionary(Of TKey, TValue).Clear
+
    Throw ReadOnlyException()
+
  End Sub
+

          
+
  Public Function Remove(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements IDictionary(Of TKey, TValue).Remove
+
    Throw ReadOnlyException()
+
  End Function 
+

          
+
  Public Function Remove(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).Remove
+
    Throw ReadOnlyException()
+
  End Function 
+

          
+
  Private Function ReadOnlyException() As Exception
+
    Return New NotSupportedException("コレクションは読み取り専用です")
+
  End Function
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
foo => 16
+
bar => 72
+

          
+
ハンドルされていない例外: System.NotSupportedException: コレクションは読み取り専用です
+
   場所 ReadOnlyDictionary`2.set_Item(TKey key, TValue val)
+
   場所 Sample.Main()
+
}}
+

          
 
#navi(..)
#navi(..)