C#introdotnet

Collections essentials

The three workhorse collections β€” a growable list, a keyed dictionary, and a uniqueness-enforcing set β€” plus the collection-expression syntax that builds them all the same way.

List<T> grows on demand, Dictionary<K,V> maps keys to values with TryGetValue, and HashSet<T> keeps only distinct items β€” and [ ... ] collection expressions initialise any of them.

Picking a collection

Most C# programs reach for one of three generic collections from System.Collections.Generic. A List<T> is a resizable array: items keep insertion order and you index them by position with list[i]. A Dictionary<TKey, TValue> maps keys to values for fast lookup by key β€” think of it as a lookup table. A HashSet<T> stores an unordered bag of distinct values and answers "is this present?" quickly. Each is generic, so List<int>, Dictionary<string, int>, and HashSet<string> are all strongly typed.

Adding, indexing, and iterating

You add to a List<T> with Add, read its length from Count, and walk it with foreach. A dictionary is keyed: dict[key] = value sets an entry, and reading a missing key throws β€” which is why TryGetValue is the safe way to look one up. It returns a bool for "was it found?" and hands the value back through an out parameter, so a single call both tests and fetches without a second lookup. A HashSet<T> ignores duplicate Add calls, making it the natural tool for de-duplication; Add even returns false when the item was already present.

Collection expressions

Since C# 12 the [ ... ] collection expression initialises any of these from a literal list of elements, with the compiler inferring the right construction from the target type. List<int> nums = [3, 1, 2]; builds a list; the same [ ... ] shape works for a HashSet<int> or an array. The spread operator .. splices another sequence in: [..first, ..second] concatenates. It is the modern, terse replacement for new List<int> { 3, 1, 2 }.

Common operations

List<T> carries the operations you reach for daily: Sort() orders it in place (with an optional comparison), Find returns the first element matching a predicate, Contains tests membership, and IndexOf reports a position. These take Predicate<T> or Comparison<T> delegates, usually written as lambdas β€” list.Find(x => x > 3). Reach for LINQ when you want a new sequence instead of mutating in place; reach for these when you want to change the list you already have.

Try it 5 examples

Build a list and iterate it

C#intro
0ms
Count: 3First: apple- apple- banana- cherry
Reference Β· output compiled offline, not runnable in-browser

Count reports 3 after the three Add calls and fruits[0] is apple, confirming a List<T> preserves insertion order so positional indexing and foreach both walk the items in the order added.