C# Cheatsheet

Collections and LINQ

Use this C# reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Arrays, List<T>, and Collection Expressions

int[] fixedScores = [90, 80, 100];  // collection expression (C# 12)
fixedScores[0] = 95;

List<int> scores = [90, 80];
scores.Add(100);
scores.RemoveAt(0);
Console.WriteLine(scores.Count);

int[] merged = [..fixedScores, ..scores, 42];  // spread
int[] empty = [];

Arrays have fixed length. List<T> grows and is the default everyday sequence type. Collection expressions target arrays, lists, spans, and any collection interface.

Dictionary and HashSet

var counts = new Dictionary<string, int>();
counts["apple"] = 1;
counts["apple"]++;

if (counts.TryGetValue("apple", out int c)) {
    Console.WriteLine(c);
}

foreach (var (word, count) in counts) {
    Console.WriteLine($"{word}: {count}");
}

var seen = new HashSet<string>();
bool added = seen.Add("csharp");     // false on duplicates
bool member = seen.Contains("csharp");

Use dictionaries for key-to-value lookup and sets for uniqueness/membership. Both are O(1) average per operation.

Queue, Stack, and PriorityQueue

var q = new Queue<string>();
q.Enqueue("first");
string head = q.Peek();
string served = q.Dequeue();

var stack = new Stack<int>();
stack.Push(1);
int top = stack.Pop();

var pq = new PriorityQueue<string, int>();
pq.Enqueue("low", 5);
pq.Enqueue("urgent", 1);
string next = pq.Dequeue();  // "urgent": lowest priority value first

Queues are FIFO; stacks are LIFO. PriorityQueue is a min-heap: negate the priority (or pass a reversed IComparer) for max-heap behavior.

Sorted Collections

var ranks = new SortedDictionary<string, int>();  // keys kept in order, O(log n)
ranks["beta"] = 2;
ranks["alpha"] = 1;                               // iterates alpha, beta

var window = new SortedSet<int> { 5, 1, 3 };
int min = window.Min;
int max = window.Max;
var slice = window.GetViewBetween(2, 5);          // {3, 5}

Reach for these when you need ordered iteration or min/max under inserts and removals, the classic interview substitute for a balanced BST.

Sorting and Comparers

int[] a = [5, 1, 4];
Array.Sort(a);                            // ascending in place
Array.Sort(a, (x, y) => y.CompareTo(x));  // descending via comparison

var people = new List<(string Name, int Age)> { ("Ada", 36), ("Alan", 41) };
people.Sort((p, q) => p.Age.CompareTo(q.Age));  // List<T>.Sort, in place

var byName = people.OrderBy(p => p.Name).ToList();  // LINQ, returns a new list

Array.Sort/List<T>.Sort are in-place and unstable; OrderBy allocates a new sequence and is stable.

LINQ Core Methods

using System.Linq;

int[] nums = [1, 2, 3, 4, 5];

var evens = nums.Where(n => n % 2 == 0).ToList();
int[] squares = nums.Select(n => n * n).ToArray();
int sum = nums.Sum();
double avg = nums.Average();
int count = nums.Count(n => n > 2);
int first = nums.First(n => n > 3);            // throws if none match
int maybe = nums.FirstOrDefault(n => n > 99);  // 0 if none match

bool anyEven = nums.Any(n => n % 2 == 0);      // true
bool allPositive = nums.All(n => n > 0);       // true
bool hasThree = nums.Contains(3);

int[][] nested = [[1, 2], [3], [4, 5]];
int[] flat = nested.SelectMany(inner => inner).ToArray();  // [1, 2, 3, 4, 5]

int[] uniq = new[] { 1, 1, 2, 2, 3 }.Distinct().ToArray(); // [1, 2, 3]
int productAll = nums.Aggregate(1, (acc, n) => acc * n);   // 120

var taken = nums.Skip(1).Take(2).ToList();                 // [2, 3]
var chunks = nums.Chunk(2).ToList();                       // [1,2], [3,4], [5]
var zipped = nums.Zip(["a", "b", "c"]).ToList();           // (1,a), (2,b), (3,c)

Where filters, Select transforms; terminal methods such as ToList, Count, Sum, and First execute the query. Everything before them is lazy (deferred execution), so materialize with ToList/ToArray before iterating twice.

MinBy, MaxBy, ToDictionary, ToLookup

var people = new[] { (Name: "Ada", Age: 36), (Name: "Alan", Age: 41) };

var oldest = people.MaxBy(p => p.Age);     // (Alan, 41)
var youngest = people.MinBy(p => p.Age);   // (Ada, 36)

Dictionary<string, int> ages = people.ToDictionary(p => p.Name, p => p.Age);
var byFirstLetter = people.ToLookup(p => p.Name[0]);  // like a grouped dictionary

MaxBy/MinBy return the element, not the key; ToDictionary throws on duplicate keys, ToLookup groups them instead.

LINQ Grouping and Ordering

var words = new[] { "bee", "ant", "bat", "cat" };

var grouped = words.GroupBy(w => w[0]);
foreach (var g in grouped) {
    Console.WriteLine($"{g.Key}: {string.Join(",", g)}");  // b: bee,bat ...
}

var countsByLength = words
    .GroupBy(w => w.Length)
    .ToDictionary(g => g.Key, g => g.Count());

var sorted = words.OrderBy(w => w.Length).ThenBy(w => w).ToList();
var reversed = words.OrderByDescending(w => w).ToList();

LINQ chains should stay readable. If the chain needs comments between every step, a loop may be clearer.