Chapter 07 Csharp 5 Bonus Features
2 min readChapter 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
foreachloop 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:
foreachintroduces 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, notfor. forstill 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.