Table of Contents
- Preface
- Acknowledgements
- Introduction
- About the Author
- The Foundation (C# 1–4)
- Why revisit the foundation?
- Features in this chapter
- Generics
- Variance
- Nullable value types
- Iterators and yield return
- LINQ and its dependencies
- Extension methods
- Lambda expressions (and a nod to anonymous methods)
- LINQ itself
- Expression trees
- Everyday conveniences
- var
- Object and collection initializers
- Anonymous types
- Auto-implemented properties
- Named and optional parameters
- Dynamic binding
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Why revisit the foundation?
- Expressiveness & Boilerplate Reduction
- What this chapter is really about
- Features in this chapter
- Shrinking the top of the file
- File-scoped namespaces
- Global and implicit usings
- Top-level statements and file-based apps
- Strings, reimagined
- Basic interpolation through raw strings
- Constant interpolated strings
- Numeric and default literals
- Digit separators and binary literals
- The default literal
- Member concision
- Expression-bodied members
- Local and static local functions
- Object construction
- Target-typed new
- Primary constructors
- Collections and variadic arguments
- Collection expressions
- params enhancements
- The field keyword
- Summary
- Key Terms
- Practice Exercises
- What's Next
- What this chapter is really about
- Data Modeling & Functional Techniques
- Data as values
- Features in this chapter
- Tuples and discards
- Tuples
- Discards
- Records — the crown jewel
- record class vs record struct vs readonly record struct
- Init-only properties
- Required members
- With-expressions
- Readonly members on structs
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Data as values
- Evolution of Control Flow (Pattern Matching)
- Why pattern matching mattered
- Features in this chapter
- Type patterns
- Property patterns
- Switch expressions
- Relational and logical patterns
- List patterns
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Why pattern matching mattered
- Type System & OOP Flexibility
- Why the type system kept growing
- Features in this chapter
- Default interface methods
- Static abstract members and generic math
- Generic constraints
- Ref struct interfaces
- Extension members
- Small additions that matter
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Why the type system kept growing
- Safety & Robustness
- What the compiler started catching for you
- Features in this chapter
- Nullable reference types
- The null-conditional family
- ?. and ?[]
- ??=
- ?.=
- Exception filters
- CallerArgumentExpression
- Checked user-defined operators
- System.Threading.Lock
- Summary
- Key Terms
- Practice Exercises
- What's Next
- What the compiler started catching for you
- Memory & Allocation Control
- Why allocation became a language feature
- Features in this chapter
- ref returns, ref locals, ref readonly
- Span<T> — the core abstraction
- ref struct and the rules around them
- stackalloc in safe contexts
- Memory<T> for async-compatible buffers
- ArrayPool<T> and MemoryPool<T>
- Ranges and indices
- UTF-8 string literals
- allows ref struct and the C# 14 papercut fixes
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Why allocation became a language feature
- Low-Level & Interop Primitives
- The last layer between managed and native
- Features in this chapter
- Native integers — nint and nuint
- Function pointers — delegate*
- Unmanaged constructed types
- [StructLayout] for binary protocols
- [LibraryImport] — modern P/Invoke
- Summary
- Key Terms
- Practice Exercises
- What's Next
- The last layer between managed and native
- Asynchronous Programming Evolution
- Why async is hard and why C# fixed it
- Features in this chapter
- async/await foundation
- The async state machine
- When to elide async
- ValueTask<T> and when to use it
- Async streams — IAsyncEnumerable<T>
- IAsyncDisposable
- ConfigureAwait and SynchronizationContext
- Cancellation patterns
- Timeouts
- Linked tokens
- Registration callbacks
- Task combinators
- Task.WhenAll
- Task.WhenAny
- Task.WhenEach (.NET 9)
- Summary
- Key Terms
- Practice Exercises
- What's Next
- Why async is hard and why C# fixed it
- Compiler & Tooling Integration
- The meta-layer
- Features in this chapter
- Caller info attributes
- Module initializers
- Source generators
- The generator project
- The incremental generator pipeline
- The output
- Interceptors
- Partial members: properties, constructors, events
- Partial properties (C# 13)
- Partial constructors (C# 14)
- Partial events (C# 14)
- Summary
- Key Terms
- Practice Exercises
- What's Next
- The meta-layer
- Modern C# in Practice — Capstone (7 Patterns)
- Why these patterns
- Patterns in this chapter
- Pattern 1: Immutable Data
- Pattern 2: Null-Safe API
- Pattern 3: Zero-Allocation
- Pattern 4: Scripting
- Pattern 5: Source Generator
- Pattern 6: Generic Math
- Pattern 7: Async Pipeline
- Closing — what you've built up
- Summary
- Key Terms
- Practice Exercises (final)
- The end
- Why these patterns
- Appendix A — Migration & Adoption Guide
- 1. LangVersion vs. TargetFramework
- Per-project overrides
- When to bump TargetFramework
- 2. Enabling Nullable Reference Types Incrementally
- Strategy A: File by file (for small teams or solo projects)
- Strategy B: Project by project (for mid-size teams)
- Strategy C: annotations-only first, then warnings (for large codebases)
- Common warnings and how to handle them
- Reflection-based libraries
- 3. Using .editorconfig to Enforce Modern Idioms
- Integrating with CI
- 4. Adoption Priority Matrix
- Tier 1 — Immediate (adopt today, no excuses)
- Tier 2 — Next Sprint (plan, pilot, roll out)
- Tier 3 — Planned (next quarter, specific scenarios)
- Tier 4 — When Needed (specific demand, not speculative)
- Adoption audit checklist
- Closing
- 1. LangVersion vs. TargetFramework
- Appendix B — Feature Quick-Reference Index
- Alphabetical Feature List
- Legend
- How to use this index
- Appendix C — Exercise Solutions
- Chapter 1 — Answers
- Basic — TradeRepository<T>.Remove
- Intermediate — Top-3 tickers by total notional
- Chapter 2 — Answers
- Basic — Primary-constructor rewrite of OldConfigService
- Intermediate — [Flags] FilePermissions + Describe helper
- Chapter 3 — Answers
- Basic — OldTrade → record CompactTrade
- Intermediate — VersionedTrade.Amend()
- Chapter 4 — Answers
- Basic — switch-statement ClassifyRisk → switch expression
- Intermediate — CalculateDiscount with relational patterns
- Chapter 5 — Answers
- Basic — Default interface methods with virtual dispatch
- Intermediate — Generic Median<T> with INumber<T>
- Chapter 6 — Answers
- Basic — ChatConfigOld rewritten with ??=
- Intermediate — Guard.Against.MatchesRegex
- Chapter 7 — Answers
- Basic — Stackalloc/ArrayPool rewrite of ProcessSensorBatchHeap
- Intermediate — Zero-allocation IPv4 parser
- Chapter 8 — Answers
- Basic — [DllImport] → [LibraryImport] conversion
- Intermediate — PacketHeaderV2 with checksum at offset 14
- Chapter 9 — Answers
- Basic — Task<T> → ValueTask<T> on cache hit
- Intermediate — Task.WhenEach progress indicator
- Chapter 10 — Answers
- Basic — Timed() with caller-info attributes
- Intermediate — [AutoEquals] generator shape
- Chapter 11 — Answers
- Basic — Immutable Data Pattern on a DTO
- Intermediate — Source Generator Pattern (reflection → switch)
- Running the answers
- Chapter 1 — Answers
- Index