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
34: LINQ Query and Method Syntax
A few years ago, I was reviewing a pull request from a junior dev who was trying to filter a list of active subscriptions based on a set of complex business rules. He had written about forty lines of code: nested foreach loops, multiple temporary List<T> objects to hold intermediate results, and a tangle of if statements. It worked, but it was a nightmare to read. I remember showing him how to do the exact same thing in three lines of LINQ. The look of absolute disbelief on his face—followed by the realization that he'd spent four hours writing something that could be a single expression—is why I love teaching this topic.
The Declarative Nature of Query Syntax
When you first see LINQ Query Syntax, it feels like someone smuggled SQL into C#. It's a declarative style, meaning you describe what you want to happen rather than how to loop through the memory. You start with from, apply filters with where, and finish with a select.
Imagine we have a list of Employee objects and we only want the names of people in the "Engineering" department who earn more than $80,000. In query syntax, it looks like this:
var highEarners = from emp in employees
where emp.Department == "Engineering" && emp.Salary > 80000
select emp.FullName;
I find that query syntax is often more readable when you're dealing with complex joins or grouping. It reads like a sentence, which makes it easier for another developer (or you, six months from now) to understand the intent of the filter without tracing loop counters.
Chaining Logic with Method Syntax
While query syntax is elegant, you'll find that most professional C# codebases lean heavily on Method Syntax. This is the "fluent" approach where you chain extension methods together using lambda expressions. It's more compact and, more importantly, it gives you access to a much wider array of LINQ operators that aren't available in the query-style syntax—like .First(), .Any(), or .Distinct().
That same employee filter looks like this using method syntax:
var highEarners = employees
.Where(emp => emp.Department == "Engineering" && emp.Salary > 80000)
.Select(emp => emp.FullName);
Notice the => (lambda operator). You're essentially passing a tiny, anonymous function into the Where method. The Where method handles the looping logic internally; you just provide the rule. It's a shift in mindset from "Iterate through this list" to "Filter this stream of data."
Choosing the Right Tool for the Job
Here is a secret: the C# compiler actually translates query syntax into method syntax before the code is ever run. They are functionally identical in terms of performance. So, why have both? It comes down to readability and capability.
I usually follow these rules of thumb:
- Use Query Syntax when you are performing complex joins or when the query is so long that the "SQL-like" structure makes it easier to scan.
- Use Method Syntax for almost everything else. If you just need to filter a list, find a single element, or transform a few properties, method chaining is faster to write and more common in the industry.
Don't feel like you have to pick a "side" and stick to it religiously. In a single file, you might use a query for a complex data retrieval and a simple .Any() call to check if the result set is empty. The goal is clarity, not consistency for the sake of consistency.
📋 Practical Task
Building a High-Value Order Filtering System
You are working on a warehouse management system. You have a list of Order objects, where each order has an OrderId (int), a CustomerName (string), a TotalAmount (decimal), and a IsShipped (bool) property.
Your task is to create a feature that identifies "Priority Pending Orders." Write a piece of code that does the following:
- Filters the list to find orders that have not been shipped (
IsShipped == false). - Further filters those orders to only include those with a
TotalAmountgreater than $500. - Sorts the resulting orders by
TotalAmountin descending order. - Returns only the
CustomerNameof these orders.
Requirement: Implement this logic twice—once using Query Syntax and once using Method Syntax—to ensure you are comfortable switching between the two patterns.
There are no comments for now.