Chapter 07 Csharp 5 Bonus Features

2 min read
Rapid overview

Chapter 07 — C# 5 bonus features

Purpose of this chapter: cover the “extra” C# 5 features beyond async/await that show up in real code and interviews: foreach closure capture behavior and caller info attributes.


7.1 Capturing foreach iteration variables

This is a classic closure pitfall.

Pre-C# 5 behavior:

  • A foreach loop effectively used a single iteration variable that changed each iteration.
  • Lambdas captured that single variable, so all delegates could observe the final value (surprising).

C# 5+ behavior:

  • foreach introduces a new iteration variable per loop iteration (matching developer intuition).
  • Capturing the iteration variable now behaves “as expected”.

Important nuance:

  • This change applies to foreach, not for.
  • for still has a single loop variable by default; capturing it can still bite you.

Senior interview takeaway:

  • Understand that closures capture variables, not values.
  • Know how to fix capture issues: create a local copy inside the loop (var copy = x;) and capture the copy.

7.2 Caller information attributes

7.2.1 Basic behavior

C# 5 introduced compiler-supplied caller info for optional parameters:

  • [CallerFilePath]
  • [CallerLineNumber]
  • [CallerMemberName]

They apply to parameters (typically optional parameters). If the caller omits the argument, the compiler supplies the caller’s file path, line number, or member name.

Why this matters:

  • It’s a low-overhead way to capture call-site metadata without stack trace inspection.

7.2.2 Logging use case

Caller info is a natural fit for logging:

  • Avoids building stack traces just to get “where did this log come from?”
  • More robust than stack trace approaches in the face of inlining/optimizations.

Practical approach in .NET apps:

  • Add your own logger extension methods that accept optional parameters annotated with these attributes.

7.2.3 Simplifying INotifyPropertyChanged

In UI model/viewmodel code, you often raise PropertyChanged with a property name:

  • [CallerMemberName] can supply the property name automatically from the setter.

Benefit:

  • Removes string literals and reduces rename/refactor bugs.

7.2.4 Corner cases

Things to keep in mind:

  • The attributes “fill in” values only when the argument is omitted.
  • You can still pass an explicit value (useful for wrappers/forwarding).
  • Defaults are not the point; the compiler-supplied values are.

7.2.5 Using with older frameworks

Considerations:

  • The attributes live in the framework (and in some cases can be polyfilled via packages), so targeting older frameworks may require conditional compilation or compatibility packages.

Interview angle:

  • Treat this as a boundary/tooling feature: it’s great for observability, but not something your domain logic should depend on.