C# OrderedDictionaryの使い方|順序を保持するDictionaryとの違いと実装例

はじめに

C#で「キーを使って値を取り出したい」「でも追加した順番どおりに表示したい」という場面では、通常のDictionary<TKey, TValue>だけでは扱いに迷うことがあります。たとえば、設定項目、メニュー項目、CSVの列、画面に表示する入力項目などは、キー検索だけでなく「並び順」も重要です。

そのようなときに使えるのがOrderedDictionaryです。OrderedDictionaryは、キーと値のペアを管理しながら、要素の順序も保持できるコレクションです。Microsoftのドキュメントでも、OrderedDictionaryはキーまたはインデックスでアクセスできるキー/値ペアのコレクションとして説明されています。

この記事では、C#のOrderedDictionaryの基本的な使い方、DictionarySortedDictionaryとの違い、実装例、注意点、型安全に扱う代替方法まで解説します。

1. C#のOrderedDictionaryとは?順序を保持できる連想配列の基本

1-1. OrderedDictionaryの概要

OrderedDictionaryは、キーと値をペアで保持するコレクションです。名前のとおり、要素の順序を意識して扱える点が特徴です。

通常の連想配列のようにキーを指定して値を取得できるだけでなく、リストのようにインデックスを指定して値を取得することもできます。

C#
using System;
using System.Collections.Specialized;

class Program
{
static void Main()
{
OrderedDictionary items = new OrderedDictionary();

items.Add("apple", "りんご");
items.Add("banana", "バナナ");
items.Add("orange", "オレンジ");

Console.WriteLine(items["banana"]); // バナナ
Console.WriteLine(items[0]); // りんご
}
}

このように、OrderedDictionaryは「キー検索」と「順序管理」を同時に扱いたい場合に便利です。

1-2. Dictionaryとの最大の違いは「追加順・インデックス順」を扱えること

Dictionary<TKey, TValue>は、キーを使って値を高速に取得するためのコレクションです。一方で、Dictionary<TKey, TValue>の列挙順は仕様として順序管理を目的にしたものではなく、Microsoftのドキュメントでも、列挙時に返される項目の順序は未定義と説明されています。

それに対してOrderedDictionaryは、要素をインデックスでも扱えるため、登録した順番や任意の位置への挿入を意識した処理ができます。

C#
OrderedDictionary users = new OrderedDictionary();

users.Add("u001", "田中");
users.Add("u002", "佐藤");
users.Add("u003", "鈴木");

Console.WriteLine(users[0]); // 田中
Console.WriteLine(users["u002"]); // 佐藤

「キーで検索できるDictionary」と「順番を持つList」の中間のような使い方ができるのが、OrderedDictionaryの大きな特徴です。

1-3. キーと値に加えてインデックスでもアクセスできる仕組み

OrderedDictionaryでは、次の2種類のアクセスができます。

C#
items["apple"] // キーでアクセス
items[0] // インデックスでアクセス

キーでアクセスする場合は、通常の辞書と同じように指定したキーに対応する値を取得します。インデックスでアクセスする場合は、0から始まる位置番号を指定して値を取得します。

C#
OrderedDictionary scores = new OrderedDictionary();

scores.Add("math", 90);
scores.Add("english", 85);
scores.Add("science", 92);

Console.WriteLine(scores["english"]); // 85
Console.WriteLine(scores[2]); // 92

注意点として、インデックスで取得できるのは基本的に「値」です。キーも一緒に扱いたい場合は、foreachDictionaryEntryを使うのが一般的です。

1-4. どの名前空間を使うか:System.Collections.Specialized

OrderedDictionaryを使うには、System.Collections.Specialized名前空間を使用します。

C#
using System.Collections.Specialized;

クラス名はOrderedDictionaryですが、System.Collections.GenericではなくSystem.Collections.Specializedに含まれている点に注意してください。非ジェネリック版のOrderedDictionarySystem.Collections.Specializedに属し、キーと値はobjectとして扱われます。

2. C#でOrderedDictionaryを使う準備

2-1. usingに必要な名前空間

C#でOrderedDictionaryを使うには、ファイルの先頭に次のusingを追加します。

C#
using System.Collections.Specialized;

foreachでキーと値を取り出す場合は、DictionaryEntryを使うために次の名前空間も追加しておくと便利です。

C#
using System.Collections;

基本形は次のとおりです。

C#
using System;
using System.Collections;
using System.Collections.Specialized;

2-2. OrderedDictionaryのインスタンス作成方法

OrderedDictionaryのインスタンスは、次のように作成します。

C#
OrderedDictionary dictionary = new OrderedDictionary();

初期化してすぐに要素を追加できます。

C#
OrderedDictionary settings = new OrderedDictionary();

settings.Add("Theme", "Dark");
settings.Add("FontSize", 14);
settings.Add("Language", "ja-JP");

Addメソッドは、指定したキーと値を持つ要素をOrderedDictionaryに追加します。Microsoftのドキュメントでも、Addは指定されたキーと値のエントリをコレクションに追加するメソッドとして説明されています。

2-3. キーと値の型はobjectになる点に注意

OrderedDictionaryの大きな注意点は、キーと値がジェネリック型ではなくobjectとして扱われることです。

C#
OrderedDictionary data = new OrderedDictionary();

data.Add("id", 1);
data.Add("name", "山田");
data.Add("isActive", true);

このように、さまざまな型の値を入れることはできます。しかし、値を取り出すときにはobjectとして返されるため、必要に応じてキャストが必要です。

C#
int id = (int)data["id"];
string name = (string)data["name"];
bool isActive = (bool)data["isActive"];

型が違う場合は実行時エラーになるため、取り出す値の型を正しく把握しておく必要があります。

2-4. ジェネリック型のDictionaryとの使い勝手の違い

Dictionary<TKey, TValue>は、キーと値の型を明確に指定できます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.Add("math", 90);
scores.Add("english", 85);

int mathScore = scores["math"];

一方、OrderedDictionaryは次のように型を指定できません。

C#
OrderedDictionary scores = new OrderedDictionary();

scores.Add("math", 90);
scores.Add("english", 85);

int mathScore = (int)scores["math"];

つまり、OrderedDictionaryは順序管理に便利ですが、型安全性ではDictionary<TKey, TValue>のほうが優れています。

3. OrderedDictionaryの基本的な使い方

3-1. Addで要素を追加する

要素を追加するにはAddメソッドを使います。

C#
OrderedDictionary fruits = new OrderedDictionary();

fruits.Add("apple", "りんご");
fruits.Add("banana", "バナナ");
fruits.Add("orange", "オレンジ");

追加した順序は保持されます。そのため、あとでループ処理をすると、基本的に追加した順番どおりに取り出せます。

C#
foreach (DictionaryEntry item in fruits)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

出力例は次のようになります。

apple: りんご
banana: バナナ
orange: オレンジ

3-2. キーを指定して値を取得・更新する

キーを指定して値を取得するには、インデクサーを使います。

C#
Console.WriteLine(fruits["apple"]); // りんご

値を更新する場合も同じです。

C#
fruits["apple"] = "林檎";

Console.WriteLine(fruits["apple"]); // 林檎

この書き方はDictionaryに近いため、辞書型に慣れている人であれば直感的に使えます。

ただし、存在しないキーを指定した場合の動作には注意が必要です。値がnullの場合とキーが存在しない場合を区別しにくいことがあるため、事前にContainsで確認すると安全です。Microsoftのドキュメントでも、特定のキーが存在するか確認するにはContainsを使う説明があります。

3-3. インデックスを指定して値を取得する

OrderedDictionaryでは、インデックス番号を指定して値を取得できます。

C#
Console.WriteLine(fruits[0]); // 林檎
Console.WriteLine(fruits[1]); // バナナ
Console.WriteLine(fruits[2]); // オレンジ

インデックスは0から始まります。1番目の要素は0、2番目の要素は1です。

C#
for (int i = 0; i < fruits.Count; i++)
{
Console.WriteLine(fruits[i]);
}

順序どおりに値だけを処理したい場合は、このようなインデックスアクセスが便利です。

3-4. foreachで順序どおりにループ処理する

foreachでキーと値を取り出す場合は、DictionaryEntryを使います。

C#
foreach (DictionaryEntry item in fruits)
{
Console.WriteLine($"キー: {item.Key}, 値: {item.Value}");
}

出力例です。

キー: apple, 値: 林檎
キー: banana, 値: バナナ
キー: orange, 値: オレンジ

OrderedDictionaryは非ジェネリックのコレクションなので、foreach (var item in fruits)と書くこともできますが、キーと値を明確に扱うならDictionaryEntryを指定したほうが読みやすくなります。

3-5. Countで要素数を確認する

要素数を確認するにはCountプロパティを使います。

C#
Console.WriteLine(fruits.Count); // 3

要素数を使ってインデックスループを回すこともできます。

C#
for (int i = 0; i < fruits.Count; i++)
{
Console.WriteLine($"{i}: {fruits[i]}");
}

インデックスアクセスを使う場合は、0以上Count - 1以下の範囲でアクセスする必要があります。

4. OrderedDictionaryの要素を操作する方法

4-1. Insertで指定位置に要素を挿入する

Insertメソッドを使うと、指定したインデックス位置に要素を挿入できます。Microsoftのドキュメントでも、Insertは指定したインデックスに新しいエントリを挿入するメソッドとして説明されています。

C#
OrderedDictionary menus = new OrderedDictionary();

menus.Add("home", "ホーム");
menus.Add("contact", "お問い合わせ");

menus.Insert(1, "about", "会社概要");

foreach (DictionaryEntry menu in menus)
{
Console.WriteLine($"{menu.Key}: {menu.Value}");
}

出力例です。

home: ホーム
about: 会社概要
contact: お問い合わせ

途中に要素を差し込めるため、表示順を細かく制御したい場合に便利です。

4-2. Removeでキーを指定して削除する

キーを指定して要素を削除するにはRemoveを使います。

C#
menus.Remove("about");

削除後にループすると、指定したキーの要素が取り除かれています。

C#
foreach (DictionaryEntry menu in menus)
{
Console.WriteLine($"{menu.Key}: {menu.Value}");
}

存在しないキーを削除しようとした場合の動作を安全に扱いたいときは、事前にContainsで確認しておくとよいでしょう。

C#
if (menus.Contains("about"))
{
menus.Remove("about");
}

4-3. RemoveAtでインデックスを指定して削除する

インデックスを指定して削除するにはRemoveAtを使います。

C#
menus.RemoveAt(0);

この場合、先頭の要素が削除されます。

C#
for (int i = 0; i < menus.Count; i++)
{
Console.WriteLine(menus[i]);
}

インデックスを使って削除する場合は、範囲外の値を指定しないように注意が必要です。

C#
int index = 1;

if (index >= 0 && index < menus.Count)
{
menus.RemoveAt(index);
}

4-4. Containsでキーの存在確認をする

キーが存在するか確認するにはContainsを使います。

C#
if (menus.Contains("contact"))
{
Console.WriteLine(menus["contact"]);
}

存在しないキーにアクセスすると意図しない結果やエラーの原因になることがあります。特に、値としてnullを許可している場合は、「キーが存在しない」のか「値がnullなのか」を判断しにくいため、Containsで確認する習慣をつけると安全です。

4-5. Clearで全要素を削除する

すべての要素を削除するにはClearを使います。

C#
menus.Clear();

Console.WriteLine(menus.Count); // 0

一時的に作成した順序付きデータを初期化したい場合や、再利用したい場合に使えます。

5. OrderedDictionaryとDictionary・SortedDictionary・Listの違い

5-1. Dictionaryとの違い:順序保証とインデックスアクセス

Dictionary<TKey, TValue>は、キーから値を高速に取得するためのコレクションです。Microsoftのドキュメントでは、Dictionary<TKey, TValue>はハッシュテーブルとして実装され、キーによる値の取得が非常に高速であると説明されています。

一方、OrderedDictionaryはキーだけでなくインデックスでもアクセスできます。

C#
OrderedDictionary data = new OrderedDictionary();

data.Add("first", "最初");
data.Add("second", "2番目");

Console.WriteLine(data["first"]); // キーで取得
Console.WriteLine(data[0]); // インデックスで取得

キー検索だけで十分ならDictionary<TKey, TValue>、順序や位置も重要ならOrderedDictionaryが候補になります。

5-2. SortedDictionaryとの違い:キー順ではなく追加順を保持する

SortedDictionary<TKey, TValue>は、キーの順序に基づいて要素を管理するコレクションです。Microsoftのドキュメントでは、内部ツリーを使ってソート順を維持し、列挙時にもソート順が維持されると説明されています。

一方、OrderedDictionaryはキーの大小ではなく、追加した順番や挿入した位置を重視します。

C#
OrderedDictionary ordered = new OrderedDictionary();

ordered.Add("c", "C");
ordered.Add("a", "A");
ordered.Add("b", "B");

foreach (DictionaryEntry item in ordered)
{
Console.WriteLine(item.Key);
}

出力例です。

c
a
b

キー順に並べたいならSortedDictionary<TKey, TValue>、登録順や表示順を維持したいならOrderedDictionaryが向いています。

5-3. Listとの違い:キー検索と順序管理を両立できる

List<T>は順序を持つコレクションです。インデックスでアクセスでき、追加順に並びます。

C#
List<string> names = new List<string>();

names.Add("田中");
names.Add("佐藤");

Console.WriteLine(names[0]); // 田中

しかし、List<T>はキーによる検索を標準で持っているわけではありません。特定のIDに対応する値を探すには、ループやLINQで検索する必要があります。

OrderedDictionaryであれば、順序を保ちながらキー検索もできます。

C#
OrderedDictionary users = new OrderedDictionary();

users.Add("u001", "田中");
users.Add("u002", "佐藤");

Console.WriteLine(users["u002"]); // 佐藤
Console.WriteLine(users[0]); // 田中

5-4. それぞれの使い分け早見表

コレクション主な特徴向いているケース
Dictionary<TKey, TValue>キー検索が速い順序が重要ではなく、型安全に扱いたい場合
OrderedDictionaryキーとインデックスでアクセスできる追加順・表示順を保持しながらキー検索したい場合
SortedDictionary<TKey, TValue>キー順に並ぶキーの昇順でデータを扱いたい場合
List<T>インデックス順に管理するキー検索よりも並び順や単純な一覧管理が重要な場合

OrderedDictionaryは、表示順とキー検索を同時に扱いたい場合に便利です。ただし、非ジェネリックで型安全性が低いため、型を厳密に扱いたい場合は代替手段も検討しましょう。

5-5. 順序を保持するDictionaryが必要なケースとは

順序を保持するDictionaryが必要になる代表的なケースは、次のような場面です。

・設定項目を登録順に表示したい
・メニューを追加順に出力したい
・CSVの列順を保持したい
・フォーム項目を表示順に管理したい
・キーで検索しながら、画面表示順も維持したい

たとえば、画面に表示する項目を管理する場合、キーは内部処理で使い、順序は表示に使うことがあります。

C#
OrderedDictionary fields = new OrderedDictionary();

fields.Add("name", "氏名");
fields.Add("email", "メールアドレス");
fields.Add("phone", "電話番号");

このようなデータは、OrderedDictionaryと相性がよいです。

6. OrderedDictionaryの実装例

6-1. 設定項目を登録順に表示する例

アプリケーションの設定項目を、登録した順番に表示する例です。

C#
using System;
using System.Collections;
using System.Collections.Specialized;

class Program
{
static void Main()
{
OrderedDictionary settings = new OrderedDictionary();

settings.Add("Theme", "Dark");
settings.Add("FontSize", 14);
settings.Add("Language", "ja-JP");

foreach (DictionaryEntry setting in settings)
{
Console.WriteLine($"{setting.Key}: {setting.Value}");
}
}
}

出力例です。

Theme: Dark
FontSize: 14
Language: ja-JP

設定ファイルや管理画面で、登録順に項目を表示したい場合に使えます。

6-2. メニュー項目を追加順に出力する例

ナビゲーションメニューを追加順に出力する例です。

C#
OrderedDictionary menu = new OrderedDictionary();

menu.Add("home", "ホーム");
menu.Add("service", "サービス");
menu.Add("company", "会社情報");
menu.Add("contact", "お問い合わせ");

foreach (DictionaryEntry item in menu)
{
Console.WriteLine($"<li><a href='/{item.Key}'>{item.Value}</a></li>");
}

出力例です。

HTML
<li><a href='/home'>ホーム</a></li>
<li><a href='/service'>サービス</a></li>
<li><a href='/company'>会社情報</a></li>
<li><a href='/contact'>お問い合わせ</a></li>

キーをURLに使い、値を表示名に使うようなケースに向いています。

6-3. CSVの列順を保持してデータを扱う例

CSVでは列の順番が重要になることがあります。OrderedDictionaryを使うと、列名と値を保持しながら列順も管理できます。

C#
OrderedDictionary row = new OrderedDictionary();

row.Add("Id", 1);
row.Add("Name", "山田太郎");
row.Add("Email", "yamada@example.com");

for (int i = 0; i < row.Count; i++)
{
Console.Write(row[i]);

if (i < row.Count - 1)
{
Console.Write(",");
}
}

出力例です。

1,山田太郎,yamada@example.com

キーで列名を管理しつつ、出力時はインデックス順に処理できる点が便利です。

6-4. JSON風データを順序付きで管理する例

JSONのようなキーと値のデータを、順序付きで管理したい場合にも使えます。

C#
OrderedDictionary user = new OrderedDictionary();

user.Add("id", 1001);
user.Add("name", "佐藤花子");
user.Add("role", "admin");

Console.WriteLine("{");

int index = 0;
foreach (DictionaryEntry item in user)
{
string comma = index < user.Count - 1 ? "," : "";
Console.WriteLine($" \"{item.Key}\": \"{item.Value}\"{comma}");
index++;
}

Console.WriteLine("}");

出力例です。

JSON
{
"id": "1001",
"name": "佐藤花子",
"role": "admin"
}

実際のJSON処理では専用ライブラリを使うべきですが、順序付きのキー/値データを簡単に組み立てる例として理解しやすい使い方です。

6-5. キー検索と表示順が両方必要な画面実装例

フォーム項目の表示順を管理しながら、キーで特定の項目を取り出す例です。

C#
OrderedDictionary formFields = new OrderedDictionary();

formFields.Add("name", "氏名");
formFields.Add("email", "メールアドレス");
formFields.Add("password", "パスワード");

// 表示順に出力
foreach (DictionaryEntry field in formFields)
{
Console.WriteLine($"表示項目: {field.Value}");
}

// 特定のキーを検索
if (formFields.Contains("email"))
{
Console.WriteLine($"emailの表示名: {formFields["email"]}");
}

このように、画面表示では順序を使い、内部処理ではキーを使う設計に向いています。

7. OrderedDictionaryを使うときの注意点

7-1. ジェネリックではないため型安全性が低い

OrderedDictionaryは非ジェネリックのコレクションです。そのため、キーや値の型をコンパイル時に厳密にチェックできません。

C#
OrderedDictionary data = new OrderedDictionary();

data.Add("count", 10);
data.Add("name", "商品A");
data.Add("enabled", true);

このように異なる型を混在させることはできますが、取り出すときに型を間違えると実行時エラーになります。

C#
// 実行時エラーの原因になる
string count = (string)data["count"];

型を安全に扱いたい場合は、Dictionary<TKey, TValue>や独自実装を検討しましょう。

7-2. 値の取得時にキャストが必要になる

OrderedDictionaryから取得した値はobjectです。そのため、具体的な型として使うにはキャストが必要です。

C#
OrderedDictionary product = new OrderedDictionary();

product.Add("id", 1);
product.Add("name", "ノートPC");
product.Add("price", 120000);

int id = (int)product["id"];
string name = (string)product["name"];
int price = (int)product["price"];

キャストが多くなるとコードが読みづらくなり、型変換エラーも起きやすくなります。値の型がすべて同じなら、代替方法を使ったほうが保守しやすくなります。

7-3. 存在しないキーへのアクセスに注意する

存在しないキーを指定して値を取得しようとすると、意図しない動作につながることがあります。安全に扱うには、先にContainsで確認します。

C#
if (product.Contains("price"))
{
int price = (int)product["price"];
Console.WriteLine(price);
}
else
{
Console.WriteLine("priceキーは存在しません。");
}

値としてnullを扱う可能性がある場合も、キーの存在確認を明示したほうが安全です。

7-4. 同じキーを追加すると例外が発生する

OrderedDictionaryでは、同じキーを重複して追加できません。Microsoftのドキュメントでも、OrderedDictionaryの各キーは一意である必要があると説明されています。

C#
OrderedDictionary users = new OrderedDictionary();

users.Add("u001", "田中");

// 同じキーを追加すると例外
users.Add("u001", "佐藤");

既存のキーの値を変更したい場合は、Addではなくインデクサーで更新します。

C#
users["u001"] = "佐藤";

重複追加を避けたい場合は、事前にContainsを使います。

C#
if (!users.Contains("u001"))
{
users.Add("u001", "田中");
}
else
{
users["u001"] = "田中";
}

7-5. 大量データではパフォーマンスを考慮する

OrderedDictionaryは順序とキー検索を両立できる便利なコレクションですが、大量データを扱う場合はパフォーマンスを考慮する必要があります。

特に、途中への挿入や削除を頻繁に行う場合、要素の位置管理が必要になります。キー検索だけが目的で順序が不要ならDictionary<TKey, TValue>のほうが適していることが多いです。

C#
// 順序が不要ならDictionaryのほうがシンプル
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.Add("math", 90);
scores.Add("english", 85);

順序が必要な理由が明確な場合にOrderedDictionaryを選ぶとよいでしょう。

8. 型安全に順序付きDictionaryを扱う代替方法

8-1. ListとDictionaryを組み合わせる方法

型安全に順序付きの辞書を扱いたい場合、List<TKey>Dictionary<TKey, TValue>を組み合わせる方法があります。

C#
List<string> order = new List<string>();
Dictionary<string, string> values = new Dictionary<string, string>();

void AddItem(string key, string value)
{
if (!values.ContainsKey(key))
{
order.Add(key);
values.Add(key, value);
}
}

AddItem("home", "ホーム");
AddItem("about", "会社概要");
AddItem("contact", "お問い合わせ");

foreach (string key in order)
{
Console.WriteLine($"{key}: {values[key]}");
}

この方法なら、Dictionary<string, string>として型安全に値を扱いながら、List<string>で順序を管理できます。

8-2. 独自クラスで順序付きDictionaryを実装する方法

処理が複雑になる場合は、独自クラスにまとめると使いやすくなります。

C#
public class OrderedMap<TKey, TValue>
{
private readonly List<TKey> keys = new List<TKey>();
private readonly Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>();

public void Add(TKey key, TValue value)
{
if (values.ContainsKey(key))
{
throw new ArgumentException("同じキーが既に存在します。");
}

keys.Add(key);
values.Add(key, value);
}

public TValue this[TKey key]
{
get => values[key];
set => values[key] = value;
}

public IEnumerable<KeyValuePair<TKey, TValue>> Items()
{
foreach (TKey key in keys)
{
yield return new KeyValuePair<TKey, TValue>(key, values[key]);
}
}
}

使用例です。

C#
OrderedMap<string, int> scores = new OrderedMap<string, int>();

scores.Add("math", 90);
scores.Add("english", 85);

foreach (var item in scores.Items())
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

型安全性と順序管理を両立したい場合に有効です。

8-3. .NETのバージョンによって使えるOrderedDictionary<TKey,TValue>を確認する

.NETの新しいバージョンでは、ジェネリック版のOrderedDictionary<TKey, TValue>も確認できます。Microsoftのドキュメントでは、System.Collections.Generic.OrderedDictionary<TKey,TValue>はキーまたはインデックスでアクセスできるキー/値ペアのコレクションとして説明されています。

ジェネリック版では、次のようにキーと値の型を指定できます。

C#
using System.Collections.Generic;

OrderedDictionary<string, int> scores = new OrderedDictionary<string, int>();

scores.Add("math", 90);
scores.Add("english", 85);

int math = scores["math"];

ただし、利用できるかどうかはターゲットフレームワークや開発環境によって異なります。プロジェクトで使用している.NETのバージョンを確認してから採用しましょう。

8-4. サードパーティライブラリを使う選択肢

標準ライブラリだけで要件を満たしにくい場合は、サードパーティライブラリを使う選択肢もあります。

たとえば、次のような要件がある場合です。

・型安全な順序付きDictionaryが必要
・高速な挿入、削除、検索が必要
・大量データを扱う
・並び替えや範囲検索も必要
・独自実装を保守したくない

ただし、ライブラリを追加すると依存関係が増えます。小規模な用途であれば、ListDictionaryの組み合わせや独自クラスで十分な場合もあります。

8-5. OrderedDictionaryを使うべきか判断する基準

OrderedDictionaryを使うべきかどうかは、次の基準で判断できます。

・追加順や表示順が重要か
・キーで値を取得する必要があるか
・インデックスで値を取得する必要があるか
・型安全性よりも手軽さを優先できるか
・扱うデータ量は大きすぎないか

次のような場合はOrderedDictionaryが向いています。

・設定項目を登録順に表示したい
・メニューを順序付きで管理したい
・CSV列の順序を維持したい
・小規模なキー/値データを順序付きで扱いたい

逆に、型安全性を重視する場合や大量データを扱う場合は、Dictionary<TKey, TValue>SortedDictionary<TKey, TValue>、独自実装、ジェネリック版OrderedDictionary<TKey,TValue>などを検討しましょう。

9. OrderedDictionaryでよくあるエラーと解決策

9-1. 「型を変換できません」と表示される原因

OrderedDictionaryの値はobjectとして返されます。そのため、間違った型にキャストするとエラーになります。

C#
OrderedDictionary data = new OrderedDictionary();

data.Add("age", 30);

// ageはintなのでstringには変換できない
string age = (string)data["age"];

正しくは次のようにします。

C#
int age = (int)data["age"];

文字列として使いたい場合は、ToString()を使います。

C#
string ageText = data["age"].ToString();

ただし、値がnullの可能性がある場合はnullチェックを入れましょう。

C#
object value = data["age"];

if (value != null)
{
string text = value.ToString();
}

9-2. 「同じキーが既に存在します」と表示される原因

同じキーをAddで追加すると例外が発生します。

C#
OrderedDictionary users = new OrderedDictionary();

users.Add("id", 1);
users.Add("id", 2); // エラー

追加前にキーの存在を確認しましょう。

C#
if (!users.Contains("id"))
{
users.Add("id", 1);
}

既存の値を更新したい場合は、次のように書きます。

C#
users["id"] = 2;

Addは新規追加、インデクサー代入は更新、という使い分けを意識するとエラーを避けやすくなります。

9-3. foreachでDictionaryEntryを使わないと値を取得できない問題

OrderedDictionaryforeachで処理するとき、キーと値を扱うにはDictionaryEntryを使います。

C#
foreach (DictionaryEntry item in users)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

次のようにKeyValuePair<string, int>などで受け取ろうとすると、OrderedDictionaryは非ジェネリックなのでうまく扱えません。

C#
// OrderedDictionaryではこの書き方は適さない
foreach (KeyValuePair<string, int> item in users)
{
}

OrderedDictionaryではSystem.Collections.DictionaryEntryを使う、と覚えておきましょう。

9-4. インデックス範囲外エラーの原因

存在しないインデックスを指定するとエラーになります。

C#
OrderedDictionary items = new OrderedDictionary();

items.Add("a", "A");
items.Add("b", "B");

Console.WriteLine(items[2]); // 範囲外

要素数が2の場合、使えるインデックスは01です。安全にアクセスするには、Countを使って範囲チェックします。

C#
int index = 2;

if (index >= 0 && index < items.Count)
{
Console.WriteLine(items[index]);
}
else
{
Console.WriteLine("指定したインデックスは範囲外です。");
}

9-5. nullキーを追加できない問題

OrderedDictionaryでは、キーにnullを使うことはできません。キーは要素を識別するための値なので、必ず一意で有効な値を指定する必要があります。

C#
OrderedDictionary data = new OrderedDictionary();

data.Add(null, "value"); // エラーの原因

キーがnullになる可能性がある場合は、追加前にチェックします。

C#
string key = GetKey();

if (key != null)
{
data.Add(key, "value");
}
else
{
Console.WriteLine("キーがnullのため追加できません。");
}

空文字もキーとしては使えますが、実装上の意味が分かりにくくなることがあります。キーには、"id""name""email"のように、意味のある文字列を使うのがおすすめです。

まとめ

OrderedDictionaryは、C#で順序を保持しながらキーと値を管理できる便利なコレクションです。通常のDictionary<TKey, TValue>がキー検索を得意とするのに対し、OrderedDictionaryはキーだけでなくインデックスでもアクセスできる点が大きな特徴です。

特に、設定項目、メニュー、CSV列、フォーム項目、JSON風データなど、表示順や登録順が重要なデータを扱う場面で役立ちます。

一方で、OrderedDictionaryは非ジェネリックであり、キーや値がobjectとして扱われます。そのため、値の取得時にはキャストが必要になり、型安全性はDictionary<TKey, TValue>より低くなります。

使い分けの目安は次のとおりです。

・キー検索だけでよいなら Dictionary<TKey, TValue>
・キー順に並べたいなら SortedDictionary<TKey, TValue>
・単純な順序付き一覧なら List<T>
・キー検索と表示順の両方が必要なら OrderedDictionary
・型安全な順序付き辞書が必要なら List + Dictionary や OrderedDictionary<TKey,TValue> を検討

OrderedDictionaryは、用途を選べば非常に便利です。順序を保持するDictionaryが必要になったときは、型安全性やパフォーマンスも考慮しながら、最適なコレクションを選びましょう。