Chapter roadmap
Design abstractions with interfaces, write reusable generic code, and query in-memory data with expressive LINQ pipelines. Work through the concepts in order, type every example yourself, and keep a short debugging log that records predictions, errors, fixes, and what each fix taught you.
Learning objectives
- Define focused interfaces
- Use generic types and constraints
- Compose LINQ operations
- Understand deferred execution
05.1
Interfaces as contracts
An interface names a capability without choosing one implementation. Keep interfaces focused so consumers depend only on operations they use. Multiple implementations can serve production, testing, or alternate storage without changing the calling logic.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplepublic interface IRepository<T>
{
T? FindById(int id);
IReadOnlyList<T> GetAll();
void Add(T item);
}ResultAny repository implementation must provide lookup, listing, and add behavior.
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
05.2
Generic types and constraints
Generics preserve type safety while sharing an algorithm or data structure across types. Constraints state required capabilities such as a base type, interface, reference type, value type, or parameterless constructor. Add constraints only when the implementation actually uses them.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplestatic T MaxBy<T, TKey>(IEnumerable<T> items, Func<T, TKey> selector)
where TKey : IComparable<TKey>
{
return items.MaxBy(selector)
?? throw new InvalidOperationException("Sequence is empty");
}ResultThe same method can select the greatest student, product, or event by a comparable key.
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
05.3
LINQ filtering and projection
LINQ methods such as Where, Select, OrderBy, and GroupBy form readable pipelines over IEnumerable<T>. Each step returns a new sequence description. Name lambda parameters clearly and split a long pipeline into meaningful intermediate queries.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplevar honorNames = students
.Where(student => student.Average >= 90)
.OrderByDescending(student => student.Average)
.Select(student => student.Name)
.ToList();ResultA materialized list of honor students ordered by average.
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
05.4
Deferred execution and aggregation
Most LINQ sequence operators use deferred execution: the source is read when the result is enumerated. ToList materializes a snapshot. Aggregates such as Count, Sum, Average, Any, and All execute immediately and require a deliberate policy for empty sequences.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
ExampleIEnumerable<int> passing = scores.Where(score => score >= 70);
scores.Add(95);
Console.WriteLine(passing.Count());
List<int> snapshot = passing.ToList();
ResultThe deferred query sees later source changes; the list snapshot does not.
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
Chapter assignments
Complete these in order. Later assignments assume that earlier skills are working. Do not only test the sample values; design tests that challenge boundaries, missing values, incorrect types, empty collections, and other likely failures.
- A5.1
Define a focused interface with two implementations and a fake used by a test or demonstration.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A5.2
Write generic utilities for swapping, selecting bounds, and grouping items with appropriate constraints.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A5.3
Create LINQ reports using Where, Select, OrderBy, GroupBy, and aggregate operators over a realistic dataset.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A5.4
Demonstrate deferred execution versus materialization and explain when a stable snapshot is required.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
Chapter project
Inventory Reporting Service
Inventory Reporting Service โ Depend on an inventory repository interface, query products with LINQ, group by category, calculate stock value, identify reorder needs, and expose immutable report records. Provide both in-memory and file-style repository implementations.
Required process
- Write a short specification listing inputs, outputs, rules, and failure cases.
- Break the work into small functions, queries, modules, or classes appropriate to the language.
- Build the smallest working version before adding optional features.
- Test normal, boundary, empty, and invalid cases and record the results.
- Refactor names and duplication, then write a concise user guide.
Key terms
- interface
- contract
- generic
- type parameter
- constraint
- LINQ
- lambda
- deferred execution
- materialization