CodeGym /Courses /C# SELF /Collections: OrderedDictionary and ReadOnlySet

Collections: OrderedDictionary and ReadOnlySet

C# SELF
Level 34, Lesson 1
Available

1. Collection OrderedDictionary

.NET has always been proud of its rich standard library, but sometimes devs had to make compromises: you want a dictionary that remembers the order you added stuff, or you need a set of immutable unique values. Before .NET 9, you had to use third-party libraries or roll your own (some folks even opened GitHub and copied OrderedDictionary from there — shhh, we won't tell anyone).

With .NET 9, we got new universal collections — now you can forget about hacks! Let's break down the two most useful ones: OrderedDictionary<TKey, TValue> and ReadOnlySet<T>.

1. What is OrderedDictionary?

OrderedDictionary is a hybrid between a dictionary and a list. It stores "key-value" pairs like a regular Dictionary<TKey, TValue>, but guarantees that the order of elements matches the order you added them. This is super important for tasks where you need to loop through items in the same order the user entered them, or when order affects business logic: printing reports, serializing data, generating configs.

Analogy

If a regular dictionary is like a cabinet with a bunch of slots where you can quickly stash and grab stuff without caring about order, then OrderedDictionary is a neat cabinet with pull-out drawers. Everything is in order, and you always know what you put in first and what came after.

2. Main difference from Dictionary

  • Dictionary: element order is NOT guaranteed (even if it looks like it is — don't trust it, it's an illusion!).
  • OrderedDictionary: elements are stored exactly in the order you added them.

3. Syntax and basic methods

Here's a basic usage example:


using System.Collections.Generic;

var od = new OrderedDictionary<string, int>();
od.Add("Ivan", 5);
od.Add("Svetlana", 8);
od.Add("Alex", 3);

// Looping in the order of adding:
foreach (var pair in od)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}

Output:

Ivan: 5
Svetlana: 8
Alex: 3

If you try the same example with a regular dictionary, the order will often be different. But with OrderedDictionary — it's always guaranteed to be the way you added it.

OrderedDictionary implements the same interfaces as a regular dictionary:

  • IDictionary<TKey, TValue>
  • IReadOnlyDictionary<TKey, TValue>
  • IEnumerable<KeyValuePair<TKey, TValue>>

4. Access by index and by key

Special feature: OrderedDictionary has an indexer by key and by numeric index!


// By name (key):
int svetlanaScore = od["Svetlana"]; // 8

// By index:
var firstEntry = od.ElementAt(0); // KeyValuePair<string, int>("Ivan", 5)

5. Updating and removing

If you try to add a new element with an existing key, you'll get an exception. If you want to replace the value — use the indexer:

od["Ivan"] = 10; // Updates the existing one, order stays the same

You can remove by key or by index:


od.Remove("Svetlana");
od.RemoveAt(0);      // removes "Ivan"

6. Visualizing the structure

Each node is a key-value pair, and the arrows show the order they were added.

7. Gotchas and mistakes

A lot of devs try to use Dictionary<TKey, TValue> and hope for order — but that's a trap! Even if the order sticks for some data, next time it might change (especially with .NET updates or on another platform).

With OrderedDictionary, because it keeps track of order, some operations (like inserting or removing from the middle) are a tiny bit slower than with a regular dictionary, but for most tasks the benefits outweigh that.

If you need to look up by index a lot — that's what OrderedDictionary is for. If you just want fast key lookups and don't care about order — use a regular dictionary.

8. Practical example for an app

Let's say in our employee and department tracking app, we need a report where employees are listed in the order they were added:


var employeeScores = new OrderedDictionary<string, int>();
employeeScores.Add("Petr", 100);
employeeScores.Add("Anna", 150);
employeeScores.Add("Viktoria", 80);

// Now the report is always in the right order:
foreach (var pair in employeeScores)
{
    Console.WriteLine($"{pair.Key}: {pair.Value} points");
}

2. Collection ReadOnlySet<T>

1. What is ReadOnlySet?

ReadOnlySet<T> is an immutable set of unique values. Before, if you wanted a "read-only set", you had to return a copy via .ToHashSet() or make your own wrapper. Now there's a collection you can't add to or remove from after it's created. This makes your code safer and prevents accidental changes from the outside.

Analogy

It's like a notebook where you wrote down unique names and... laminated the pages. Nobody can add, rip out, or change anything anymore.

2. Creating a ReadOnlySet

The easiest way to make an immutable set is to use the extension method:


var colors = new[] { "Red", "Green", "Blue", "Green" };
var readOnlyColors = colors.ToReadOnlySet();

// Now it's strictly unique values and can't be changed:
foreach (var color in readOnlyColors)
    Console.WriteLine(color);

Output:

Red
Green
Blue

You can also use the direct constructor (if you need to):


var set = new ReadOnlySet<int>(new[] {1, 2, 2, 3, 5, 1}); // Still just 1,2,3,5

ReadOnlySet in Microsoft Docs

3. Main properties and methods

  • Read-only (immutable)
  • Count — number of elements
  • Contains(item) — check if an item exists
  • Supports LINQ queries (IEnumerable<T>)
  • Fast lookups, but doesn't support add, remove, or clear

Example:


if (readOnlyColors.Contains("Red"))
    Console.WriteLine("Red is in the list!");

You can't do this:


// Compile error!
readOnlyColors.Add("Yellow");

4. Where is this useful?

Very often you want to expose info as a set, but don't want anyone to accidentally (or maliciously) change it. For example:

  • Returning a list of supported user roles
  • List of allowed file extensions
  • List of unique config parameters that shouldn't be changed

Example in our app: let's say there's a list of allowed departments:


public static ReadOnlySet<string> Departments { get; } =
    new[] { "HR", "Development", "Accounting" }.ToReadOnlySet();

Now nobody can mess with the department list from outside.

5. Visualization: difference from regular HashSet

HashSet can be changed, ReadOnlySet — never!

3. OrderedDictionary vs. ReadOnlySet: comparison table

Collection Stores pairs? Keeps order Can be changed Key lookup Index lookup Scenarios
OrderedDictionary Yes, "key-value" Yes Yes Yes Yes Maps, settings, reports
ReadOnlySet No, just values No No Yes No Safe sets, constants
HashSet No, just values No Yes Yes No Sets where you need to modify
Dictionary Yes, "key-value" No Yes Yes No Maps with no order requirements
2
Task
C# SELF, level 34, lesson 1
Locked
Access by Index and Removing an Element in OrderedDictionary
Access by Index and Removing an Element in OrderedDictionary
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION