Land your first dev job — 100% money-back guarantee.
Continue

LINQ in C#

Query and transform data with LINQ — method syntax, query syntax, Where, Select, OrderBy, GroupBy, Join, Aggregate, deferred vs immediate execution, and custom extension operators.

LINQ Introduction

LINQ (Language Integrated Query) lets you query any collection — arrays, lists, XML, databases — using the same syntax, directly in C#. It operates on anything that implements `IEnumerable<T>`. There are two equivalent styles: method syntax (chained extension methods) and query syntax (SQL-like keywords). Most developers prefer method syntax; both compile to the same IL.

1. Method Syntax vs Query Syntax

Both syntaxes produce identical results. Method syntax uses chained extension method calls; query syntax uses `from`, `where`, `select` keywords.

2. Deferred vs Immediate Execution

LINQ queries are lazy — they describe what to do, not when. The query runs only when you iterate it or call a terminal operator like `ToList()`, `Count()`, or `First()`.

Core LINQ Operators

The core LINQ operators cover the operations you will use in nearly every query. `Where` filters, `Select` transforms, `OrderBy`/`ThenBy` sort, and element operators (`First`, `Last`, `Single`, `ElementAt`) pick specific items. Each returns `IEnumerable<T>` (except element operators which return `T`).

1. Filtering & Projecting

`Where` filters elements by a predicate. `Select` projects each element to a new shape. `SelectMany` flattens nested sequences.

2. Sorting

`OrderBy` / `OrderByDescending` for primary sort. `ThenBy` / `ThenByDescending` for secondary sort. `Reverse` flips the order.

3. Element Operators

Pick a single element from a sequence. `First`/`Last` for the first/last match, `Single` when exactly one match is expected, `ElementAt` for index access.

Grouping & Joining

Grouping and joining are among the most powerful LINQ operations. `GroupBy` partitions a sequence into groups — like SQL `GROUP BY`. `Join` combines two sequences on a matching key — like SQL `INNER JOIN`. `GroupJoin` produces a left outer join where each left element is paired with all matching right elements.

1. GroupBy

`GroupBy` partitions elements into `IGrouping<TKey, TElement>` groups. Each group has a `Key` and is itself an `IEnumerable<TElement>`.

2. Join & GroupJoin

`Join` is an inner join — only elements with matches on both sides are included. `GroupJoin` is a left outer join — every left element appears, even with no right matches.

Aggregation & Set Operations

LINQ aggregates reduce a sequence to a single value: `Count`, `Sum`, `Min`, `Max`, `Average`, and the general-purpose `Aggregate`. Set operations treat sequences as mathematical sets: `Distinct` removes duplicates, `Union` merges two sequences, `Intersect` keeps common elements, `Except` removes elements.

1. Aggregate Operators

Standard numeric aggregates plus the general `Aggregate` for custom accumulation.

2. Set Operations

`Distinct`, `Union`, `Intersect`, and `Except` treat sequences as mathematical sets — duplicates are automatically handled.

Partitioning, Quantifiers & Conversion

Partitioning operators slice a sequence: `Skip`/`Take` for pagination, `SkipWhile`/`TakeWhile` for condition-based slicing. Quantifiers check the entire sequence and return a bool: `Any`, `All`, `Contains`. Conversion operators materialise lazy sequences into concrete collections.

1. Skip, Take & Pagination

`Skip(n)` discards the first n elements. `Take(n)` keeps only the first n. Combined, they implement pagination.

2. Quantifiers & Conversion

`Any`, `All`, `Contains` answer yes/no questions. Conversion operators like `ToList`, `ToArray`, `ToDictionary`, `ToLookup` materialise results.

Generation & Combining

Not all LINQ starts from an existing collection. The `Enumerable` class provides static methods to generate sequences on the fly. Combining operators let you stitch sequences together in different ways — end-to-end, element-by-element, or by adding single items.

1. Enumerable.Range, Repeat & Empty

`Range` generates a sequence of consecutive integers. `Repeat` repeats a value n times. `Empty` produces a typed empty sequence — useful as a safe starting point.

2. Concat, Append & Prepend

`Concat` joins two sequences end-to-end. `Append` adds one element to the end. `Prepend` adds one element to the front. All return a new sequence — the originals are unchanged.

3. Zip

`Zip` pairs elements from two (or three) sequences by position. It stops at the shorter sequence. Useful for combining parallel data sources.

Type Operators & Advanced Query Syntax

`OfType<T>` and `Cast<T>` deal with non-generic or mixed-type sequences — useful when working with legacy APIs, `ArrayList`, or heterogeneous object lists. Advanced query syntax keywords (`let`, `into`, `join...into`) make complex queries more readable by introducing intermediate variables and continuing clauses.

1. OfType, Cast & ToHashSet

`OfType<T>` filters a sequence to only elements of type T. `Cast<T>` casts all elements (throws on failure). `ToHashSet` materialises to a set with O(1) lookup.

2. Advanced Query Syntax — let, into, join…into

`let` introduces an intermediate variable inside a query. `into` continues a query after a `select` or `group`. `join...into` performs a group join in query syntax.