Chapter 03 Csharp 3 Linq
5 min read- Chapter 03 — C# 3: LINQ and everything that comes with it
- 3.1 Automatically implemented properties
- 3.2 Implicit typing
- 3.2.1 Typing terminology (what interviewers mean)
- 3.2.2 Implicitly typed local variables (`var`)
- 3.2.3 Implicitly typed arrays
- 3.3 Object and collection initializers
- 3.3.1 Why they matter
- 3.3.2 Object initializers
- 3.3.3 Collection initializers
- 3.4 Anonymous types
- 3.4.1 Syntax and behavior
- 3.4.2 Compiler-generated type
- 3.4.3 Limitations
- 3.5 Lambda expressions
- 3.5.1 Syntax
- 3.5.2 Capturing variables (closures)
- 3.5.3 Expression trees
- 3.6 Extension methods
- 3.6.1 Declaring an extension method
- 3.6.2 Invoking extension methods
- 3.6.3 Chaining calls (the LINQ style)
- 3.7 Query expressions (query syntax)
- 3.7.1 Query expressions translate from C# to C#
- 3.7.2 Range variables and transparent identifiers
- 3.8 The end result: LINQ
Chapter 03 — C# 3: LINQ and everything that comes with it
Purpose of this chapter: understand the collection of C# 3 language features that (together) enable LINQ to be expressive and practical: implicit typing, lambdas, extension methods, query syntax, etc.
3.1 Automatically implemented properties
Auto-properties remove boilerplate when a property is just a simple backing field:
public string Name { get; set; }
Interview nuance:
- Auto-properties are great for simple state exposure; once you need invariants/validation, you usually move to an explicit backing field or validation in the setter/constructor.
- In C# 3 specifically, auto-properties don’t support initializers or true read-only auto-properties (later versions do).
3.2 Implicit typing
3.2.1 Typing terminology (what interviewers mean)
Keep these terms crisp:
- Static typing vs dynamic typing: when binding/type checks happen (compile time vs runtime).
- Explicit typing vs implicit typing: whether you must write the type name in source, or the compiler infers it from context.
3.2.2 Implicitly typed local variables (var)
var is still statically typed; it just asks the compiler to infer the type:
var language = "C#"; // inferred as string
Rules worth remembering:
- Must be initialized at declaration.
- The initializer must have a compile-time type (e.g.,
var x = null;is invalid without more context).
When to use it:
- The type is obvious from the RHS (
var x = new Dictionary<string,int>();can be obvious, too). - The type is anonymous.
- The explicit type would reduce readability (very long generic types) and the variable name carries meaning.
When to avoid it:
- When the type itself is important information (e.g., distinguishing
IQueryable<T>vsIEnumerable<T>at a boundary).
3.2.3 Implicitly typed arrays
Useful when the compiler can infer a single element type:
var numbers = new[] { 1, 2, 3 }; // inferred as int[]
Interview trap:
- Mixed numeric literals can change the inferred element type (e.g.,
new[] { 1, 2L }→long[]).
3.3 Object and collection initializers
3.3.1 Why they matter
Initializers let you build objects/collections in a single expression, which:
- Reduces “set this then set that” noise.
- Makes projection code (LINQ results) much cleaner.
3.3.2 Object initializers
var p = new Person { FirstName = "Ada", LastName = "Lovelace" };
Interview nuance:
- This is still “construction + assignments” (not constructor parameters), so it doesn’t enforce invariants the way constructors do.
3.3.3 Collection initializers
var list = new List<int> { 1, 2, 3 };
3.4 Anonymous types
Anonymous types are a lightweight way to create “shape-only” results, common in projections:
var summary = new { Name = p.Name, p.Age };
3.4.1 Syntax and behavior
Key behaviors to be fluent with:
- Properties are read-only (effectively immutable from the consumer side).
- Equality is structural (based on property values) for the generated type.
3.4.2 Compiler-generated type
Practical takeaway:
- The actual runtime type exists but isn’t easily nameable; this is a major reason
varis used in LINQ projections.
3.4.3 Limitations
- Best used inside methods/queries; don’t try to leak them across public APIs.
- If you need a stable contract, define a DTO/record instead.
3.5 Lambda expressions
Lambdas are an inline way to express behavior:
Func<int, int> square = x => x * x;
3.5.1 Syntax
- Expression lambdas:
x => x * 2 - Statement lambdas:
x => { var y = x * 2; return y; }
3.5.2 Capturing variables (closures)
Closures capture variables, not values. Interview-relevant pitfalls:
- Capturing loop variables incorrectly (classic foreach/for issues in older versions).
- Capturing a mutable variable shared across async continuations.
Rule of thumb:
- Prefer creating a local copy inside the loop if you’re capturing it (or use modern C# behavior consciously).
3.5.3 Expression trees
Lambdas can be represented as:
- Delegates (executable code)
- Expression trees (data describing the code)
Why it matters:
- LINQ providers (e.g., EF) can translate expression trees to SQL instead of executing them in memory.
Interview trap:
- “Why did my query run client-side?” → often because you converted to
IEnumerable<T>too early or used a method the provider can’t translate.
3.6 Extension methods
Extension methods let you “add” methods to existing types without modifying them.
3.6.1 Declaring an extension method
public static class StringExtensions
{
public static bool IsBlank(this string? s) => string.IsNullOrWhiteSpace(s);
}
3.6.2 Invoking extension methods
Looks like an instance call:
if (input.IsBlank()) { /* ... */ }
3.6.3 Chaining calls (the LINQ style)
LINQ is largely “extension method pipelines”:
var top = products
.Where(p => p.StockCount > 0)
.OrderByDescending(p => p.Price)
.Select(p => new { p.Name, p.Price });
Interview nuance:
- Extension methods are resolved at compile time. If you accidentally use
Enumerable.vsQueryable., you can change execution location (in-memory vs provider).
3.7 Query expressions (query syntax)
Query expressions are syntax sugar over method calls. They translate to calls like Where, Select, SelectMany, Join, etc.
3.7.1 Query expressions translate from C# to C#
Practical takeaway:
- Query syntax and method syntax are equivalent; choose whichever is clearer for the specific query.
3.7.2 Range variables and transparent identifiers
When you chain multiple from/join clauses, the compiler may synthesize intermediate shapes to carry state forward.
Interview angle:
- Understanding that translation exists helps debug odd-looking types and projection behavior.
3.8 The end result: LINQ
One query can involve multiple features at once:
var(because the result type might be anonymous)- anonymous types (projection)
- query syntax (readability)
- lambdas + extension methods (translation target)
- expression trees (provider translation for
IQueryable<T>)
Key interview competency:
- Explain where code executes (database/provider vs in-memory) and why.