Chapter 15 Csharp 8 And Beyond

2 min read
Rapid overview

Chapter 15 — C# 8 and beyond

Purpose of this chapter: understand the C# 8 feature set (and nearby future items) that meaningfully change how we write safe, expressive code: nullable reference types, switch expressions and richer patterns, ranges/indexes, and deeper async integration.


15.1 Nullable reference types (NRT)

Nullable reference types are about making nullability explicit in the type system so the compiler can warn about null mistakes.

15.1.1 What problem do they solve?

They reduce:

  • NullReferenceExceptions in production.
  • “Maybe null” ambiguity in APIs.

15.1.2 Changing meaning for reference types

With NRT enabled:

  • string typically means “non-null string”.
  • string? means “may be null”.

15.1.3 Enter NRT (compiler-assisted contracts)

The compiler tracks null-state flow:

  • It warns when you might dereference null.
  • It warns when you assign a maybe-null to a non-nullable reference.

15.1.4 Compile time vs runtime

Important: NRT is primarily a compile-time feature.

  • It doesn’t change runtime behavior of references (null still exists).

15.1.5 The “damn it” / bang operator (!)

x! means “I assert this isn’t null”.

Guideline:

  • Use sparingly; treat it as a localized escape hatch when you can prove invariants but the compiler can’t.

15.1.6 Migration experience

Practical approach:

  • Enable NRT in a controlled way, fix warnings in layers, add annotations to public APIs, and keep warning counts trending down.

15.2 Switch expressions

Switch expressions make mapping logic concise and expression-oriented:

  • Great for “value in → value out” mappings.

Guideline:

  • Use when it improves clarity; don’t cram complex logic into a single expression if a statement-based switch is clearer.

15.3 Recursive pattern matching

Patterns can match deeper structure, not just top-level types.

15.3.1 Property patterns

Match based on property values (shape + constraints).

15.3.2 Deconstruction patterns

Match using Deconstruct shape.

15.3.3 Omitting types

Patterns can often infer types; use when it stays readable.


15.4 Indexes and ranges

Indexes/ranges make slicing more expressive:

  • ^1 means “from the end”.
  • a[1..^1] means a slice/range (excluding endpoints based on indices).

Guideline:

  • Great for parsing and data processing, but keep boundaries clear and test off-by-one behavior.

15.5 More async integration

15.5.1 Async disposal (await using)

Use for resources requiring async cleanup:

  • Important for I/O resources where disposal can be asynchronous.

15.5.2 Async iteration (await foreach)

Consume IAsyncEnumerable<T> streams without buffering everything in memory.

15.5.3 Async iterators

Produce async streams with async + yield return semantics (conceptually).

Interview angle:

  • These matter in high-throughput services for streaming data pipelines and backpressure-aware designs.

15.6 Features not yet in preview (book context)

The book discusses items that became real later (or evolved), e.g.:

  • Default interface methods
  • Records

Takeaway:

  • Know the “direction of travel”: safer types, richer patterns, and better async ergonomics.

15.7 Getting involved

Practical suggestion:

  • Track language proposals and adopt features deliberately; avoid churn without a payoff.