C# LINQ Tutorial for Beginners: Introduction to LINQ
LINQ lets you ask a question about your data — instead of writing a loop that manually figures out the answer.
You already know how to loop. You've written foreach loops that filter items with an if, build up a new list, and sum or count things along the way. That skill works everywhere — but it's also verbose. Every time you want "the expensive products, sorted by name" you write a new hand-rolled loop, and the actual intent — "give me the expensive products, sorted by name" — gets buried inside bookkeeping code: an empty list, an if, an Add, a comparer.
LINQ (Language Integrated Query) is a set of tools built directly into C# and .NET that lets you express that intent directly, as a query, instead of writing the loop that carries it out by hand. This lesson introduces what LINQ is, the problem it solves, and previews its two syntaxes — the rest of this module goes deep on every piece.
What Is It?
The Simple Explanation
LINQ stands for Language INtegrated Query. It's a uniform way to filter, transform, sort, group, and summarize data — collections in memory, XML, and (through a separate provider) databases — using the same small vocabulary of operators, no matter what the data actually is or where it lives.
Instead of writing "loop through this, check that condition, build a new list," you write "from this data, where this condition, select this shape" — and .NET does the looping for you.
The Technical Definition
Technically, LINQ is a set of extension methods — Where, Select, OrderBy, GroupBy, Sum, and dozens more — defined in System.Linq on IEnumerable<T> (and, separately, on IQueryable<T>, covered in the next lesson). Because you learned in Foundations that every collection — arrays, List<T>, Dictionary<TKey,TValue>, HashSet<T>, and any custom type — implements IEnumerable<T>, every one of those types gets the entire LINQ vocabulary for free, automatically, the instant you add using System.Linq; to a file.
LINQ isn't a new language feature bolted on top of C# — it's a library of methods that take an IEnumerable<T> and hand back another IEnumerable<T> (or a single value). Because it's "just methods," everything you already know about generics, Func<T,TResult>, Predicate<T>, and lambda expressions from the Intermediate module applies directly — a LINQ call like Where(p => p.Price > 50) is a method call that takes a lambda, exactly like any other method that accepts a Func<T,bool>.
Why Does It Exist?
The Problem — Manual Loops Bury Intent in Mechanics
Say you have a list of products, and you need the names of every product that's in stock and costs more than $50, sorted cheapest first. Written by hand, that's a small piece of logic wrapped in a lot of scaffolding:
var results = new List<string>();
foreach (var product in products)
{
if (product.Stock > 0 && product.Price > 50)
{
results.Add(product.Name);
}
}
results.Sort((a, b) =>
{
var pa = products.First(p => p.Name == a).Price;
var pb = products.First(p => p.Name == b).Price;
return pa.CompareTo(pb);
});
foreach (var name in results)
Console.WriteLine(name);Nothing here is wrong — but notice what you had to write to express one simple idea: an empty list to accumulate into, an if for the filter, an Add call, and then a fairly awkward custom sort because you sorted names but needed to sort by price. The actual question — "which in-stock products over $50, cheapest first?" — is buried under fifteen lines of bookkeeping.
The Solution — Say What You Want, Not How to Loop
LINQ collapses that entire block into a single, readable pipeline:
var results = products
.Where(p => p.Stock > 0 && p.Price > 50)
.OrderBy(p => p.Price)
.Select(p => p.Name);
foreach (var name in results)
Console.WriteLine(name);This reads almost like English: "from products, where in stock and price over 50, order by price, select the name." No accumulator list, no manual comparer, no separate loop for sorting. The intent is the code — that's the entire point of LINQ.
Big Picture — Before vs After
↓
Loop over source
↓
Check condition manually
↓
Add to list manually
↓
Sort with a custom comparer
↓
Loop again to project a shape
.Where(...) — describe the filter↓
.OrderBy(...) — describe the order↓
.Select(...) — describe the shape↓
.NET builds and runs the loop for you
How It Works
- Any
IEnumerable<T>— an array, aList<T>, the result of another query.
- Each operator (
Where,OrderBy,Select, ...) takes the previousIEnumerable<T>and returns a new one, wrapping more logic around it.
- A
foreach, or a call toToList()/ToArray(), actually pulls items through the whole chain, one at a time. (This "not yet, only when asked" behavior is deferred execution — its own lesson, coming up soon in this module.)
Simple Example
The dataset below — a small product catalog — will reappear throughout this module, so you see the same data queried in different ways as you learn each operator.
public record Product(int Id, string Name, string Category, decimal Price, int Stock);
List<Product> products =
[
new(1, "Wireless Mouse", "Electronics", 24.99m, 120),
new(2, "Mechanical Keyboard", "Electronics", 89.99m, 0),
new(3, "Standing Desk", "Furniture", 349.00m, 15),
new(4, "Desk Lamp", "Furniture", 19.50m, 60),
new(5, "Noise-Cancelling Headphones", "Electronics", 199.99m, 8),
new(6, "Office Chair", "Furniture", 259.00m, 0),
];
// Manual loop
var expensive1 = new List<string>();
foreach (var p in products)
if (p.Price > 50 && p.Stock > 0)
expensive1.Add(p.Name);
// LINQ — same result
var expensive2 = products
.Where(p => p.Price > 50 && p.Stock > 0)
.Select(p => p.Name)
.ToList();
// Both print: Noise-Cancelling HeadphonesCode → Meaning → Result: Where keeps only the products matching the condition; Select transforms each surviving Product into just its Name; ToList() forces the result into a concrete List<string>. Same outcome as the hand-written loop, in a third of the code.
Real-World Example
An admin dashboard for an e-commerce store needs a "low stock alert" panel: any product with fewer than 10 units left, sorted by how urgent the shortage is (fewest units first), showing just the name and stock count. Without LINQ, that's a filter, a sort, and a projection, each requiring its own loop or careful merging. With LINQ, it's one pipeline:
var lowStockAlerts = products
.Where(p => p.Stock < 10)
.OrderBy(p => p.Stock)
.Select(p => new { p.Name, p.Stock });
foreach (var item in lowStockAlerts)
Console.WriteLine($"{item.Name}: only {item.Stock} left");
// Mechanical Keyboard: only 0 left
// Office Chair: only 0 left
// Noise-Cancelling Headphones: only 8 leftThis exact shape — filter, sort, project — is one of the most common patterns in real applications: reporting screens, search results, admin panels, and API endpoints all repeatedly need "some of the data, reshaped, in some order." LINQ turns that recurring pattern into a one-line habit instead of a fresh loop every time.
Analogy
A hand-written loop is like cooking a meal entirely yourself: you go to the market, pick ingredients, chop them, combine them in the right order, and plate the result — every single step is your responsibility, and one wrong step ruins the dish.
LINQ is like ordering from a menu: "the salmon, medium, with a side salad" describes the outcome you want. The kitchen — .NET's LINQ implementation — knows how to actually produce it. You're not forbidden from cooking yourself (a hand-loop is still valid C#), but for the vast majority of everyday "filter, sort, shape" tasks, ordering from the menu is faster to write and easier to read.
Under the Hood
products.Where(p => p.Price > 50).Select(p => p.Name);
from p in products
where p.Price > 50
select p.Name;
- They are not two different features — query syntax is compiled directly into the same
Where/Selectmethod calls. Lessons 119 and 120, later in this module, cover both in full, including exactly how that translation works.
Common Confusion
1. "LINQ is a database technology" — no, it's a general query library
Many developers first meet LINQ through Entity Framework and assume it's inherently about databases. It isn't. LINQ started as, and still is, primarily a way to query in-memory collections (this is called "LINQ to Objects," and it's what this entire module focuses on). Databases are just one more source LINQ can target, through a separate provider — that's the subject of the next lesson and, in full, of Intermediate Part VI.
2. "LINQ replaces loops entirely" — it doesn't, and shouldn't
LINQ is a powerful default for "filter, transform, sort, aggregate" tasks — but a plain foreach is still the right tool for side-effecting work (like sending an email per item) or performance-critical hot paths. This module's closing lesson, LINQ Performance, covers exactly where the trade-off lies.
Common Mistakes
Mistake 1 — Forgetting using System.Linq;
Without this using directive, none of the extension methods (Where, Select, etc.) appear on your collections at all — you'll get a compiler error saying the method doesn't exist. Add using System.Linq; at the top of the file (most new project templates include it via global usings already).
Mistake 2 — Reaching for a loop out of habit, for something LINQ expresses more clearly
Writing a five-line loop with an accumulator list for a simple filter-and-project is no longer necessary once you know LINQ. Recognize the "filter / transform / sort / aggregate" shape and reach for the matching operator instead — that recognition skill is exactly what this module builds.
When Should I Use It?
Reach for LINQ when
- You're filtering, transforming, sorting, grouping, or aggregating a collection.
- Readability and intent matter more than shaving microseconds.
- You want to chain several operations together cleanly.
A plain loop may still be better when
- You need side effects per item (sending emails, writing files) rather than producing a new sequence.
- You're in a proven performance hot path where allocation and delegate overhead genuinely matter.
- The logic needs early exits or complex branching that a chain of operators would obscure, not clarify.
Mental Model
LINQ = "here's what answer I want; you figure out the walking"
Remember:
· LINQ is just extension methods on
IEnumerable<T> — nothing magic, nothing new to the runtime.· Method syntax (
.Where().Select()) and query syntax (from...where...select) are the same thing, compiled the same way.· This lesson is the front door — every operator you saw in passing here gets its own full lesson in this module.
Key Takeaway
- LINQ (Language Integrated Query) is a built-in library of query operators —
Where,Select,OrderBy, and many more — that work on anyIEnumerable<T>. - It replaces hand-written loops that filter, transform, sort, or aggregate data with short, declarative pipelines that describe what you want, not how to compute it.
- LINQ works because it's built entirely on
IEnumerable<T>— the same interface every collection you've already used implements. - There are two equivalent syntaxes — method syntax and query syntax — covered in depth later in this module.
- LINQ doesn't replace every loop — side effects and hot paths still often call for a plain
foreach.
Check Your Understanding
You've seen what LINQ is and why it exists. Let's check your understanding before diving into the individual operators.
1. What does LINQ fundamentally provide?
Show answer
Correct: B
Why B is correct: LINQ is a library of extension methods layered on top of IEnumerable<T> (and IQueryable<T>). It's ordinary C#, not a new language.
Why A is incorrect: LINQ is integrated into C# via method and optional query syntax — it isn't a separate language, and it's not database-specific.
Why C is incorrect: LINQ doesn't touch the type system; it's just methods you call.
Why D is incorrect: LINQ to Objects (this module's focus) works entirely in memory, with no database involved at all.
Reinforcement: LINQ is "just methods" — which is exactly why it works on every IEnumerable<T> you already know.
2. Why does every collection type — arrays, List<T>, Dictionary<TKey,TValue> — automatically get access to LINQ operators like Where and Select?
Show answer
Correct: B
Why B is correct: LINQ methods extend IEnumerable<T>. Any type implementing that interface — which is every standard collection, plus any custom enumerable type — gets the whole vocabulary automatically.
Why A is incorrect: There's no special-casing; it's ordinary extension method resolution, the same mechanism as any other extension method.
Why C is incorrect: No such base class exists — collections share a common interface, not a common base class.
Why D is incorrect: Method-syntax LINQ operators (Where, Select) are ordinary methods, not language keywords — only query syntax (from/where/select) involves actual C# keywords.
Reinforcement: This is the same "shared contract, universal behavior" idea from IEnumerable<T> that you learned back in Foundations — LINQ is the payoff for that design.
3. What is the main advantage LINQ offers over a hand-written foreach loop for a filter-then-sort-then-project task?
Show answer
Correct: B
Why B is correct: As the before/after comparison showed, the value of LINQ here is readability and intent, not raw speed — the same idea, expressed with far less scaffolding.
Why A is incorrect: LINQ is not inherently faster than a well-written loop, and can sometimes be marginally slower due to delegate and iterator overhead — a topic covered fully in this module's closing lesson.
Why C is incorrect: LINQ operators rely heavily on lambda expressions — they don't remove them, they use them as arguments.
Why D is incorrect: Standard LINQ to Objects runs sequentially by default; parallelism requires the separate, explicitly-opted-into PLINQ (AsParallel()), not covered in this introductory lesson.
Reinforcement: LINQ's core value proposition is clarity of intent, not automatic performance wins.
4. Which statement correctly describes the relationship between LINQ's method syntax and query syntax?
Show answer
Correct: C
Why C is correct: As previewed in "Under the Hood," from...where...select is translated by the compiler into calls like .Where(...).Select(...) — they produce identical code.
Why A is incorrect: They aren't separate features — one compiles directly into the other, which lessons 119 and 120 cover in detail.
Why B is incorrect: Both syntaxes work identically on in-memory collections and on database-backed IQueryable<T> sources — neither is restricted to one or the other.
Why D is incorrect: Neither syntax is deprecated; method syntax is simply more commonly used day-to-day, as you'll see later in this module.
Reinforcement: Two syntaxes, one engine — that equivalence is the key idea to carry forward into the rest of this module.
You now understand what LINQ is and why it exists. Next up: the distinction between IEnumerable<T> and IQueryable<T> — two very different kinds of "queryable" data.