Skip to content

Use descriptive variables in your WhenAny

Run the complete page example.

WhenAny watches several properties at once and passes each one's latest value to a selector lambda you write. Name the selector's parameters after what they mean, not after their position, so the expression reads like the rule it checks.

Name each parameter

PreferDescriptiveParameterNames watches two properties on a nested Enrollment object and combines them into one boolean:

using IDisposable subscription = student.WhenAny(
        x => x.Enrollment.IsEnabled,
        x => x.Enrollment.IsLoading,
        static (isEnabled, isLoading) => isEnabled.Value && isLoading.Value)
    .Subscribe(canJoin => log.Add(canJoin ? "Can join now" : "Cannot join yet"));

isEnabled and isLoading say what each value means, so isEnabled.Value && isLoading.Value reads as the rule it is: join only once enrollment is both enabled and finished loading.

AvoidUnnamedParameters runs the identical rule, but names the same two parameters x and y:

using IDisposable subscription = student.WhenAny(
        x => x.Enrollment.IsEnabled,
        x => x.Enrollment.IsLoading,
        static (x, y) => x.Value && y.Value)
    .Subscribe(canJoin => log.Add(canJoin ? "Can join now" : "Cannot join yet"));

Both compile, and both send the same values here:

Cannot join yet, Can join now

The difference is not in what the code does. It is in what a reader has to do to understand it: with x and y, you have to look back at the two expressions above the selector to work out which is which. With isEnabled and isLoading, the selector already tells you.

Why this matters

This guideline is about readability, not runtime behavior. It matters most in a boolean expression like the one above, where x.Value && y.Value gives no clue what either side means. It matters more the more properties a WhenAny call watches.

At a glance

DoInstead ofWhy
Name each WhenAny selector parameter after what it meansx, y positional namesThe expression reads as the rule it checks.