Scott Mitchell's An Extensive Examination of Data Structures Using C# 2.0 is a six-part series originally published on MSDN and revised in January 2005 for the then-new .NET Framework 2.0 and C# 2.0. It remains a useful guided tour of fundamental data structures, algorithmic complexity, and implementation tradeoffs.
The original companion downloads provide runnable samples and source code for the exercises in the series.
Install the runnable Data Structures 2.0 samples Download the Data Structures 2.0 source codeThe value of the series is not that every example represents how you should write C# today. Its value is that it makes the behavior of common data structures visible. Understanding why a Dictionary<TKey, TValue> can usually find an item quickly, why a binary search tree can become unbalanced, or why breadth-first search naturally uses a queue will make you better at choosing and using the modern collection types that .NET provides.
The January 2005 revision is historically interesting because it was updated for C# 2.0 and .NET Framework 2.0, when generics fundamentally changed collection programming in C#. A declaration such as List<Customer> or Dictionary<string, Customer> expresses the element types directly and avoids the casting required by older nongeneric collections.
var names = new List<string>();
var customersById = new Dictionary<int, Customer>();
var pendingJobs = new Queue<Job>();
var undoHistory = new Stack<Edit>();
var selectedIds = new HashSet<int>();The links below point to Microsoft's archived Visual Studio 2005 Technical Articles versions. Read them in order if this is your first substantial study of data structures; later parts build on terminology and ideas introduced earlier.
Part 1 introduces data structures, algorithm analysis, and the importance of comparing the cost of operations. It also examines arrays and lists.
Part 1 — An Introduction to Data StructuresPart 2 examines FIFO and LIFO structures and introduces hash-based lookup through the Hashtable type.
var byUsername = new Dictionary<string, User>();
var work = new Queue<WorkItem>();
var history = new Stack<Command>();Part 3 introduces binary trees and binary search trees, including searching, insertion, deletion, and traversal. This material is especially useful for understanding the difference between a data structure's shape and its performance guarantees.
Part 3 — Binary Trees and BSTsPart 4 explores ways to avoid the pathological behavior of an ordinary binary search tree, including balanced trees and skip lists.
Part 4 — Building a Better Binary Search TreePart 5 generalizes from trees to graphs and examines graph representation and traversal. Graph concepts remain directly relevant to routing, dependency analysis, social networks, build systems, scheduling, state transitions, and many other problems.
Part 5 — From Trees to GraphsPart 6 examines mathematical sets, general set operations, and disjoint-set structures.
Part 6 — Efficiently Representing SetsA useful way to read the series is to separate three questions: What concept is being taught? What type did .NET provide in 2005? and What would I normally choose today?
| Concept or need | Historical or instructional emphasis | Typical modern starting point |
|---|---|---|
| Resizable sequence | ArrayList, arrays, early List<T> |
List<T>; arrays when fixed-size storage is appropriate |
| Key/value lookup | Hashtable and hashing internals |
Dictionary<TKey, TValue> |
| FIFO processing | Queue structure | Queue<T>; ConcurrentQueue<T> when appropriate for concurrent access |
| LIFO processing | Stack structure | Stack<T>; ConcurrentStack<T> when appropriate for concurrent access |
| Unique values | Custom set implementation | HashSet<T> |
| Unique values in sorted order | Tree-based set ideas | SortedSet<T> |
| Sorted key/value data | Balanced-tree concepts | SortedDictionary<TKey, TValue> or SortedList<TKey, TValue> depending on workload |
| Priority-based removal | Custom heap/priority-queue concepts in older material | PriorityQueue<TElement, TPriority> |
| Graph traversal | Custom nodes, edges, queues, stacks | Domain model plus generic collections; choose representation for the problem |
| Disjoint sets | Union/find implementation | Often still a small purpose-built union-find/disjoint-set implementation or a suitable library |
When a method does not need the full concrete collection API, expose or accept the narrowest useful abstraction. Common examples include IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>, ISet<T>, and IDictionary<TKey, TValue>.
public decimal CalculateTotal(IEnumerable<OrderLine> lines)
{
return lines.Sum(line => line.Price * line.Quantity);
}The 2005 series predates much of today's collection ecosystem. Modern .NET includes concurrent and immutable collection families in addition to the familiar mutable generic collections.
One of the most current parts of the series is its emphasis on algorithmic complexity. Language syntax changes. Libraries grow. The cost of repeatedly scanning an n-element sequence is still different from the expected cost of a hash lookup, and an accidentally quadratic algorithm still becomes painful as input grows.
On the first pass, focus on vocabulary and behavior: sequence, key, hash, collision, tree height, traversal, edge, set, union, and complexity. Do not get distracted by syntax that looks old.
When the article implements or discusses a collection, identify the closest modern .NET type and compare the public operations.
Reimplementing a structure can be valuable when the learning objective is the structure itself. A small BST, graph traversal, heap, or union-find implementation forces you to confront invariants that are easy to miss when calling a library method.
Use the archived series for the conceptual journey and current Microsoft documentation for API selection and current platform behavior.
Collections and Data Structures — .NET When to Use Generic Collections — .NET Commonly Used Collection Types — .NET Collections — C# Programming GuideList<T>, Dictionary<TKey, TValue>, Queue<T>, Stack<T>, and HashSet<T> for ordinary modern C# code.