C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
35: Deferred Execution in LINQ
I've spent a lot of time reviewing PRs from junior devs, and there is one specific "aha!" moment that almost always happens around the same time: the realization that a LINQ query isn't actually a result, but a set of instructions. When you first start using LINQ, it feels like you're calling a function that returns a filtered list. It isn't.
The Idea That var query = ... Actually Runs the Query
Here is the trap. You might write some code like this, expecting the highValueOrders variable to hold a snapshot of the data at the moment you defined it:
var orders = new List<Order> {
new Order { Id = 1, Total = 100 },
new Order { Id = 2, Total = 50 }
};
// You think the filtering happens RIGHT HERE
var highValueOrders = orders.Where(o => o.Total > 80);
// Now you add a new order to the source list
orders.Add(new Order { Id = 3, Total = 200 });
foreach (var order in highValueOrders)
{
Console.WriteLine(order.Id);
}
If you think the query executed on line 6, you'd expect the output to be just 1. But when you actually run this, the output is 1 AND 3. This confuses people because it feels like the variable highValueOrders "magically" updated itself. It didn't. It never held any orders to begin with.
Thinking of LINQ as a Blueprint, Not a Result
What actually happened is called Deferred Execution. When you call .Where(), .Select(), or .OrderBy(), C# doesn't go hunting through your list. Instead, it creates an IEnumerable object—essentially a blueprint or a "to-do list" that says: "Whenever someone eventually asks me for the items, I will go to the original list and filter for totals over 80."
The query only actually executes when you iterate over it. In the example above, the execution happened inside the foreach loop. Because the source list had changed by the time the loop started, the query saw the updated data.
I'll be honest: this is a double-edged sword. On one hand, it's incredibly efficient. You can chain five different filters together, and LINQ will process each item through the whole chain one by one, rather than creating five intermediate lists in memory. On the other hand, if you accidentally put a heavy database call inside a deferred query and then iterate over that query three times in your code, you've just hit your database three times.
Forcing the Issue with Immediate Execution
If you actually want a snapshot of the data right now, you need to trigger Immediate Execution. You do this by calling a method that requires the actual values to produce a result. The most common ones are .ToList(), .ToArray(), .Count(), or .First().
Look at the difference here:
// This executes IMMEDIATELY.
// The filter runs now, and the results are stored in a new list.
var highValueOrders = orders.Where(o => o.Total > 80).ToList();
orders.Add(new Order { Id = 3, Total = 200 });
// Now, this will only print '1' because the snapshot was taken before order 3 existed.
foreach (var order in highValueOrders)
{
Console.WriteLine(order.Id);
}
My rule of thumb? If you're passing a query around to different methods or if the underlying data is going to change, call .ToList() early to avoid "ghost" data changes and unexpected performance hits.
📋 Practical Task
The Dynamic Inventory Filter Glitch
You are building a warehouse management system. You have a list of Product objects. Your task is to demonstrate that you understand the difference between deferred and immediate execution by fixing a bug in the following scenario.
The Goal: Create a "Snapshot" of out-of-stock products. Even if products are restocked (added back to the inventory) after the snapshot is taken, the snapshot should still show the products that were out of stock at the moment the snapshot was created.
public class Product {
public string Name { get; set; }
public int StockQuantity { get; set; }
}
public class Program {
public static void Main() {
var inventory = new List<Product> {
new Product { Name = "Laptop", StockQuantity = 0 },
new Product { Name = "Mouse", StockQuantity = 10 },
new Product { Name = "Keyboard", StockQuantity = 0 }
};
// TODO: Create a variable called 'outOfStockSnapshot'
// that captures all products with StockQuantity == 0.
// Ensure this uses IMMEDIATE execution so it doesn't change later.
var outOfStockSnapshot = // Your code here
// Simulating a restock: The Laptop is now back in stock
inventory[0].StockQuantity = 5;
// This should print "Laptop" and "Keyboard"
// because they were out of stock when the snapshot was taken.
foreach (var p in outOfStockSnapshot) {
Console.WriteLine(p.Name);
}
}
}
Requirements:
- Modify the
outOfStockSnapshotassignment to ensure the results are materialized immediately. - The final output must be "Laptop" and "Keyboard", despite the Laptop's stock being updated to 5.
There are no comments for now.