Chapter 10 Smorgasbord Of Features For Concise Code

2 min read
Rapid overview

Chapter 10 — A smörgåsbord of features for concise code

Purpose of this chapter: learn several features that improve day-to-day readability by removing noise: using static, initializer enhancements, the null-conditional operator, and exception filters.


10.1 Using static directives

using static imports the static members of a type, so you can omit the type name:

using static System.Math;

var radians = degrees * PI / 180;
var x = Cos(radians) * magnitude;

When it helps:

  • Math-heavy code.
  • Working with enum flags (reducing repetition).

When it hurts:

  • If it makes the origin of members unclear or introduces name collisions.

10.2 Object and collection initializer enhancements

These features reduce ceremony when building objects and collections.

10.2.1 Indexers in object initializers

When a type has an indexer, you can set entries inline:

  • Great for dictionaries and similar “keyed” objects.

10.2.2 Using extension methods in collection initializers

Collection initializers can work with Add methods, including extension methods, which can make certain builder patterns more fluent.

10.2.3 Test code vs production code

Guideline:

  • Initializers are fantastic in tests and setup code.
  • In production paths, be careful that “clever” initialization doesn’t hide complexity or errors.

10.3 The null-conditional operator (?., ?[])

Null-conditional operators let you safely dereference:

var city = customer?.Address?.City;
var first = list?[0];

10.3.1 Simple and safe dereferencing

Use it to reduce defensive if (x != null) ladders when “missing data” is acceptable.

10.3.2 More detail (mental model)

It short-circuits:

  • If the receiver is null, the whole expression becomes null (or default for nullable value contexts).

10.3.3 Boolean comparisons

A common gotcha:

  • x?.Flag == true is a safe way to check a nullable bool-like result.

10.3.4 Indexers

?[] works similarly for indexers:

  • Useful when the collection itself can be null.

10.3.5 Working effectively

Guideline:

  • Use it when null is an acceptable outcome.
  • Don’t use it to hide a bug where null is unexpected; fail fast instead.

10.3.6 Limitations

  • It’s not a replacement for correct invariants.
  • It can obscure where null originated if overused; keep expressions readable.

10.4 Exception filters (catch (...) when (...))

Exception filters let you decide whether to catch based on a condition:

catch (HttpRequestException ex) when (IsTransient(ex))
{
    // retry / fallback
}

Why they matter:

  • You can filter without catching-and-rethrowing (keeps cleaner stack traces and intent).

10.4.2 Retrying operations

Filters help with “catch only transient errors” while letting non-transient errors bubble.

10.4.3 Logging as a side effect

You can log in the filter expression (carefully):

  • It can reduce duplicated try/catch blocks.
  • Don’t make filters expensive or stateful in surprising ways.

10.4.4 Case-specific filters

Filters let you keep exception handling precise:

  • “Handle only when the error code matches X” rather than catching everything.

10.4.5 Why not just throw?

Throwing after catching is often worse:

Use filters or let exceptions propagate when you’re not meaningfully handling them.

  • It can hide intent, alter stack traces, and complicate debugging.