C#: LINQ & Collections
LINQ (Language Integrated Query) provides a unified syntax for querying arrays, lists, XML, databases, and any IEnumerable<T> source. It uses deferred execution — queries run when iterated, not when defined.
LINQ Query Syntax
var users = new List<User> { /* ... */ };
// Query syntax (SQL-like)
var result = from u in users
where u.Age >= 18
orderby u.Name
select new { u.Id, u.Name };
// Method syntax (more composable — prefer for most cases)
var result2 = users
.Where(u => u.Age >= 18)
.OrderBy(u => u.Name)
.Select(u => new { u.Id, u.Name });
// Common LINQ operators
users.Where(u => u.IsActive) // filter
users.Select(u => u.Name) // project
users.SelectMany(u => u.Orders) // flatten nested collections
users.OrderBy(u => u.Name) // sort ascending
users.OrderByDescending(u => u.Age) // sort descending
users.GroupBy(u => u.Department) // group
users.Join(orders, // inner join
u => u.Id,
o => o.UserId,
(u, o) => new { u.Name, o.Total })
users.Distinct() // remove duplicates
users.Take(10) // first N
users.Skip(20).Take(10) // pagination
users.FirstOrDefault(u => u.Id == 5) // first match or null
users.SingleOrDefault(u => u.Id == 5) // exactly one match or null
users.Any(u => u.IsAdmin) // exists
users.All(u => u.IsActive) // all match
users.Count(u => u.IsActive) // count matching
users.Sum(u => u.Orders.Count) // aggregate
users.Min(u => u.Age)
users.Max(u => u.Age)
users.Average(u => u.Score)
users.ToList() // materialize — execute queryGrouping & Joins
// GroupBy
var byDepartment = users
.GroupBy(u => u.Department)
.Select(g => new {
Department = g.Key,
Count = g.Count(),
AvgAge = g.Average(u => u.Age),
Names = g.Select(u => u.Name).ToList()
});
// Left join with GroupJoin
var usersWithOrders = users
.GroupJoin(orders,
u => u.Id,
o => o.UserId,
(user, userOrders) => new {
User = user,
Orders = userOrders.ToList(),
OrderCount = userOrders.Count()
});
// Zip — combine two sequences element-by-element
var names = new[] { "Alice", "Bob" };
var scores = new[] { 95, 82 };
var combined = names.Zip(scores, (name, score) => $"{name}: {score}");
// Deferred execution — query is not executed here
var query = users.Where(u => u.IsActive); // IEnumerable<User> — not yet run
// Executed here:
foreach (var u in query) { } // or: query.ToList(), query.Count(), etc.Collections
// List<T> — resizable array
var list = new List<string> { "a", "b", "c" };
list.Add("d");
list.Insert(0, "z");
list.Remove("b");
list.RemoveAt(0);
list.Sort();
list.BinarySearch("c"); // fast search on sorted list
// Dictionary<TKey, TValue>
var dict = new Dictionary<string, int>
{
["one"] = 1,
["two"] = 2,
};
dict["three"] = 3;
dict.TryGetValue("one", out int val); // safe get
dict.ContainsKey("four"); // check existence
foreach (var (key, value) in dict) { } // deconstruct KeyValuePair
// HashSet<T> — unique values, O(1) lookup
var set = new HashSet<int> { 1, 2, 3 };
set.Add(4);
set.Contains(2); // O(1)
set.UnionWith(new[] { 3, 4, 5 }); // union
set.IntersectWith(new[] { 2, 3 }); // intersection
set.ExceptWith(new[] { 1 }); // difference
// Queue<T> and Stack<T>
var queue = new Queue<string>();
queue.Enqueue("first");
var next = queue.Dequeue(); // FIFO
var stack = new Stack<int>();
stack.Push(1);
var top = stack.Pop(); // LIFO
// ImmutableList, ImmutableDictionary (System.Collections.Immutable)
var immutable = ImmutableList.Create(1, 2, 3);
var added = immutable.Add(4); // returns new list, original unchanged
// Span<T> — stack-allocated slice (no heap allocation)
Span<int> slice = numbers.AsSpan(2, 3); // elements 2, 3, 4Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free