.NET 9から、OrderedDictionary<TKey, TValue>クラスが加わった

.NET 9から、要素（キーとバリュー）の追加順序を保持するジェネリックなDictionary、`OrderedDictionary<TKey, TValue>`が追加されました。

* [クラスリファレンス](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ordereddictionary-2?view=net-9.0)
* [紹介公式ブログ](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/libraries#ordereddictionarytkey-tvalue)

この`OrderedDictionary<TKey, TValue>`クラスは、「キーによる高速な検索」と「順序を維持したコレクション」の両方を実現したいという時に嬉しいクラスです。

実は、.NET Frameworkには昔から、非ジェネリックなOrderedDictionaryがあったのですが、ジェネリックな順序を保持するDictionaryなクラスはなかなか公式ライブラリに登場しませんでした。

---


従来の`SortedDictionary<TKey, TValue>`と違い・

```csharp
// もともとあったSortedDictionary<TKey, TValue>だと・・・
SortedDictionary<string, string> dictionary = new();
dictionary.Add("KeyC", "ValueC");
dictionary.Add("KeyA", "ValueA");
dictionary.Add("KeyB", "ValueB");

// 従来のSortedDictionary<TKey, TValue>だと、追加順序は保持されず次のように表示される。
// [KeyA, ValueA]
// [KeyB, ValueB]
// [KeyC, ValueC]
foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

`OrderedDictionary<TKey, TValue>`を列挙すると、追加した順序を保持します。

```csharp
// .NET 9から追加されたOrderedDictionary<TKey, TValue>だと・・・
OrderedDictionary<string, string> dictionary = new();
dictionary.Add("KeyC", "ValueC");
dictionary.Add("KeyA", "ValueA");
dictionary.Add("KeyB", "ValueB");

// 従来のOrderedDictionary<TKey, TValue>だと、追加順序が保持されて次のように表示される。
// [KeyC, ValueA]
// [KeyA, ValueC]
// [KeyB, ValueB]
foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

なお、コレクション初期化子は内部でAddメソッドを読んでいるので、↑と↓は等価です。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "KeyC", "ValueC" },
    { "KeyA", "ValueA" },
    { "KeyB", "ValueB" }
};

foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

「キーによる高速な検索」と「順序を維持したコレクション」の両方を実現したい場合は、`OrderedDictionary<TKey, TValue>`が便利です。

---

以下に、`OrderedDictionary<TKey, TValue>`の特徴的なメソッドの利用例を示します。


GetAtで、指定したインデックスの`KeyValuePair<TKey, TValue>`を取得できます。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "Key0", "Value0" },
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

var index1KeyValuePair = dictionary.GetAt(1);

// [Key1, Value1]と表示される。（インデックス0始まりで1個目のKeyValuePair要素）
Console.WriteLine(index1KeyValuePair);
```

RemoveAtで、指定したインデックスの`KeyValuePair<TKey, TValue>`を取り除けます。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "Key0", "Value0" },
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

// インデックス1の要素を削除
dictionary.RemoveAt(1);

// 次のように表示される。
// [Key0, Value0]
// [Key2, Value2]
foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

InsertAtで、指定したインデックスにキーとバリューの要素を挿入できます。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "Key0", "Value0" },
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

// インデックス1に、要素をインサート
dictionary.Insert(1, "InsertedKey", "InsertedValue");

// [Key0, Value0]
// [InsertedKey, InsertedValue]
// [Key1, Value1]
// [Key2, Value2]
foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

SetAtで指定したインデックスにキーとバリュー、もしくはバリューを上書きできます。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "Key0", "Value0" },
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

// インデックス0のキーとバリューをセット
dictionary.SetAt(0, "KeyReplaced0", "ValueReplaced0");

// インデックス2のバリューだけセット
dictionary.SetAt(2, "ValueReplaced2");

// [KeyReplaced0, ValueReplaced0]
// [Key1, Value1]
// [Key2, ValueReplaced2]
foreach (var keyValuePair in dictionary)
{
    Console.WriteLine(keyValuePair);
}
```

IndexOfで指定したキーのインデックスを探索できます。

```csharp
OrderedDictionary<string, string> dictionary = new()
{
    { "Key0", "Value0" },
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

// "Key0"のインデックスを探索
var indexOfKey0 = dictionary.IndexOf("Key0");

// 0
Console.WriteLine(indexOfKey0);
```

---

ちなみに、`OrderedDictionary<TKey, TValue>`は、`IList<KeyValuePair<TKey, TValue>>`を実装しています。

```csharp
OrderedDictionary<string, string> orderedDictionary = new OrderedDictionary<string, string>
{
    { "KeyA", "ValueA" },
    { "KeyB", "ValueB" },
    { "KeyC", "ValueC" }
};

// OrderedDictionary<TKey, TValue>は、IList<KeyValuePair<TKey, TValue>>を実装している
// だから当然、これはOK
IList<KeyValuePair<string, string>> list = orderedDictionary;

// IList<KeyValuePair<string, string>>、intでの[]アクセスができる
KeyValuePair<string, string> elementIndex0 = list[1];

// OrderedDictionary<string, string>だと、TKeyであるstringでの[]アクセス
string valueWithKeyA = orderedDictionary["KeyA"];
```

普通の`Dictionary<TKey, TValue>`は、当然`IList<KeyValuePair<TKey, TValue>>`を実装していません。それはそう。