LINQ (Language Integrated Query) is a library of methods for filtering, transforming, sorting, grouping and summarizing sequences. Add using System.Linq; and every array, List<T>, Dictionary and other IEnumerable<T> gains methods like Where, Select and OrderBy, each taking a lambda that says what to do with one element.
A first query: method syntax and query syntax
LINQ has two notations. Method syntax chains calls; query syntax reads like SQL. The compiler turns query syntax into the same method calls, so they produce identical results.
Output:
95, 91, 88
95, 91, 88
Method syntax is more common in modern code, and some operators (Take, Distinct, Count, First, ToList) exist only as methods. Query syntax shines for joins and for queries that introduce intermediate variables with let. The rest of this page uses method syntax and shows query syntax where it adds something.
Where and Select: filter and project
Where keeps the elements that match a condition. Select turns each element into something else. Together they cover most everyday queries.
Output:
Mug, Kettle
MUG $8.50 | LAMP $32.00 | KETTLE $27.00 | RUG $120.00
Lamp: 38.40
Rug: 144.00
1. Mug 2. Lamp 3. Kettle 4. Rug
Where never changes elements, and Select never changes how many there are. new { p.Name, WithTax = ... } creates an anonymous type, a class the compiler writes for you, whose properties you read with normal dot syntax. It has no name you can write, so a variable holding one must be declared with var, and it suits results that stay inside one method.
OrderBy, ThenBy and sorting by several keys
Output:
Ana 60
Ana 15
Ben 90
Ben 40
Chloe 25
Ben, Ana, Ben, Chloe, Ana
OrderBy is stable: elements with equal keys stay in their original order. Use ThenBy or ThenByDescending for a second key. A second OrderBy would re-sort everything by its own key, leaving the first key as a mere tie breaker: the opposite priority. In query syntax, the keys are comma-separated: orderby o.Customer, o.Total descending.
Unlike List<T>.Sort, OrderBy leaves the source untouched and returns a new sequence.
First, Single, Last and their OrDefault versions
These return one element instead of a sequence. The plain versions throw when there is nothing to return; the OrDefault versions return the type's default value.
Output:
Ana
Ben
Bea
True
First: InvalidOperationException
Single: InvalidOperationException
0
The exception from First on an empty sequence is InvalidOperationException with the message "Sequence contains no elements" (or "Sequence contains no matching element" when a predicate matched nothing), one of the most searched C# errors. Use FirstOrDefault when "none" is a normal outcome and check for null.
The last line shows the value type trap: for an int sequence, FirstOrDefault returns 0 when nothing matches, which cannot be told apart from a real 0. Filter with Where and check Any() first, or on .NET 6 and later pass your own default: scores.FirstOrDefault(s => s > 100, -1).
Single is for "exactly one": it throws if there are none or more than one, which makes it an assertion as much as a query.
Any, All, Count and Contains
Output:
True
True
True
3
True
True
Any() stops at the first match, so items.Any() is the right way to ask "is there anything?". items.Count() > 0 reads the Count property when the source is a collection such as a list or array, but on a query or other lazy sequence it walks every element first. All on an empty sequence returns true, because there is no element that fails the condition.
Sum, Average, Min and Max
Output:
Total: 28.25
Items: 7
Priciest: 7.25
Average price: 4.92
0
Average: InvalidOperationException
Sum of an empty sequence is 0, but Average, Min and Max of an empty sequence of numbers throw InvalidOperationException: there is no sensible answer. Guard with Any(), or use DefaultIfEmpty() first: empty.DefaultIfEmpty(0).Average().
Distinct, Take and Skip
Output:
home, shop, cart
Page 1: 1 2 3 4 5 6 7 8 9 10
Page 2: 11 12 13 14 15 16 17 18 19 20
Page 3: 21 22 23
12, 15
Distinct keeps the first occurrence of each value in order. Skip(n).Take(m) is the standard paging pattern, and TakeWhile/SkipWhile stop or start at the first element that fails a condition. Enumerable.Range(start, count) generates a sequence of integers without an array.
GroupBy
GroupBy sorts elements into buckets by a key. Each group has a Key and is itself a sequence, so you can aggregate it.
Output:
North: 3 sales, total 600
South: 2 sales, total 400
East: 1 sales, total 350
Top rep: Ana 400
North -> Ana/Chloe/Ana; South -> Ben/Eli; East -> Dev
Groups come out in the order their keys first appear in the source (North, South, East), not sorted; add OrderBy(g => g.Key) if you need that. In query syntax, group X by K chooses what goes into each group (here just the rep's name) and into g names the group so the query can continue.
ToList, ToArray and ToDictionary
The To... methods run the query and store the results.
Output:
ben@x.com
2
ToDictionary: ArgumentException
ToDictionary throws ArgumentException when two elements produce the same key, just like Dictionary.Add. If duplicates are expected, group first (as wordCounts does) or use ToLookup, which maps each key to a sequence of values.
Deferred execution
Where, Select, OrderBy, GroupBy and most other operators return a query object and do no work until something enumerates it. That has two consequences worth seeing:
Output:
25, 40, 99
converting 10
converting 25
converting 40
converting 99
Count: 4
converting 10
converting 25
converting 40
converting 99
Max: 9900
converting 10
converting 25
converting 40
converting 99
Cached: 4, 9900
The first query saw 99 even though it was added after the query was written: the filter ran when string.Join enumerated it. The second query printed its conversions twice because Count() and Max() each enumerated it from scratch. With a cheap lambda that is only wasted work; with a database query, a file read, or a random number inside the lambda, the two passes can return different data. When you will use a result more than once, materialize it with ToList() or ToArray().
Operators that return a single value (Count, Sum, First, Any, Max) and the To... methods run immediately. See IEnumerable and yield for how that laziness is built.
Quick reference
| Operator | Does | Returns |
|---|---|---|
Where(x => cond) | Filter | Sequence (deferred) |
Select(x => expr) | Transform each | Sequence (deferred) |
SelectMany(x => seq) | Flatten nested sequences | Sequence (deferred) |
OrderBy, ThenBy (+Descending) | Sort, stable | Sequence (deferred) |
GroupBy(x => key) | Bucket by key | Sequence of groups (deferred) |
Distinct, Take, Skip | Dedupe, page | Sequence (deferred) |
First, Single, Last | One element, throws if none | Element |
FirstOrDefault etc. | One element or default | Element |
Any, All, Contains | Test | bool |
Count, Sum, Min, Max, Average | Aggregate | Number |
ToList, ToArray, ToDictionary | Run and store | Collection |
Common mistakes
First()on a sequence that can be empty. Throws "Sequence contains no elements"; useFirstOrDefaultand check.- Two
OrderBycalls. The second key becomes the main one; useThenBy. - Enumerating a query several times. The work repeats; call
ToList()once. - Expecting a query to snapshot its source. It reads the source when enumerated, not when written.
Average,Min,Maxon an empty sequence. They throw; guard withAny()orDefaultIfEmpty.Count() > 0to test for elements.Any()stops at the first one.
Frequently Asked Questions
What is LINQ in C#?
LINQ (Language Integrated Query) is a set of extension methods in System.Linq, such as Where, Select, OrderBy and GroupBy, that work on any IEnumerable<T>: arrays, lists, dictionaries, and results of other queries. C# also has a query syntax (from x in items where ... select ...) that the compiler translates into the same method calls.
What is the difference between Select and Where in LINQ?
Where filters: it keeps the elements for which the lambda returns true and leaves them unchanged. Select projects: it transforms every element into something else, such as a property or a new object, and keeps the count the same. They are often chained: people.Where(p => p.Age >= 18).Select(p => p.Name).
What is the difference between First and FirstOrDefault?
First() returns the first element (or the first match for a predicate) and throws InvalidOperationException ("Sequence contains no elements" or "no matching element") when there is none. FirstOrDefault() returns the type's default value instead: null for classes and 0 for int, so check the result before using it.
How do I sort by two fields with LINQ?
Chain ThenBy after OrderBy: orders.OrderBy(o => o.Customer).ThenByDescending(o => o.Total). Calling OrderBy twice does not work: the second call re-sorts the whole sequence, so its key becomes the primary one and the first key only breaks ties, the reverse of what was meant.
How does GroupBy work in LINQ?
GroupBy(x => key) returns one group per distinct key. Each group is an IGrouping<TKey, TElement>: it has a Key property and is itself a sequence of the elements with that key, so you can call Count(), Sum() or Select on it. Groups come out in the order their keys first appear.
What is deferred execution in LINQ?
Most LINQ operators do not run when you call them; they return a query that runs each time it is enumerated (by foreach, ToList(), Count(), and so on). A query therefore sees changes made to the source after it was defined, and enumerating it twice does the work twice. Call ToList() or ToArray() to run it once and keep the results.