-
-
Notifications
You must be signed in to change notification settings - Fork 6
Add WhenAnyValue support to Reactive attribute #360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Introduces WhenAnyValue and WhenAnyValueAccessModifier options to the [Reactive] attribute, enabling automatic generation of observable properties for value changes. Updates documentation and test baselines to reflect new usage and API surface.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #360 +/- ##
==========================================
+ Coverage 43.22% 43.24% +0.02%
==========================================
Files 64 64
Lines 3646 3702 +56
Branches 420 423 +3
==========================================
+ Hits 1576 1601 +25
- Misses 1950 1979 +29
- Partials 120 122 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds WhenAnyValue support to the Reactive attribute, enabling automatic generation of observable properties that track value changes for reactive properties. The implementation introduces two new properties to the Reactive attribute: WhenAnyValue (bool) to enable the feature, and WhenAnyValueAccessModifier (PropertyAccessModifier enum) to control the access level of the generated observable.
Key changes:
- Enhanced the Reactive attribute with WhenAnyValue options for automated observable generation
- Updated the source generator to create IObservable properties named "WhenAny{PropertyName}" when enabled
- Added comprehensive test coverage with a new test case demonstrating the feature
Reviewed changes
Copilot reviewed 69 out of 69 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs | Implements extraction of WhenAnyValue parameters from attributes and adds GetWhenAnyValueObservable method to generate observable properties |
| src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs | Extends PropertyInfo record with WhenAnyValue and WhenAnyValueAccessModifier fields |
| src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs | Adds WhenAnyValue and WhenAnyValueAccessModifier properties to ReactiveAttribute definition |
| src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs | Demonstrates usage by converting manual WhenAnyValue call to generated WhenAnyPeople observable |
| src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs | Adds FromReactiveWithAnyValue test case to verify WhenAnyValue observable generation |
| src/ReactiveUI.SourceGenerator.Tests/REACTIVE/*.verified.cs | Updates test baselines with new attribute properties and copyright year (2026) |
| README.md | Documents the new WhenAnyValue feature with usage examples |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| attributeData.TryGetNamedArgument("WhenAnyValueAccessModifier", out int whenAnyValueAccessModifierArgument); | ||
| var whenAnyValueAccessModifier = whenAnyValueAccessModifierArgument switch | ||
| { | ||
| 1 => "protected", | ||
| 2 => "internal", | ||
| 3 => "private", | ||
| 4 => "protected internal", | ||
| 5 => "private protected", | ||
| _ => "public", | ||
| }; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for extracting and mapping the WhenAnyValueAccessModifier enum is duplicated between the property handling (lines 161-170) and field handling (lines 313-322) sections. This code duplication makes maintenance harder and increases the risk of inconsistencies if updates are needed. Consider extracting this logic into a separate helper method that both sections can call.
| $$""" | ||
| /// <summary> | ||
| /// Gets an observable for when the <see cref="{{propertyInfo.PropertyName}}"/> property | ||
| /// changes. | ||
| /// </summary> | ||
| {{propertyInfo.WhenAnyValueAccessModifier}} global::System.IObservable<{{propertyInfo.TypeNameWithNullabilityAnnotations}}> WhenAny{{propertyInfo.PropertyName}} => | ||
| this.WhenAnyValue(x => x.{{propertyInfo.PropertyName}}); | ||
| """; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generated WhenAnyValue observable property is missing the [ExcludeFromCodeCoverage] attribute that is applied to the generated reactive properties (see lines 446 and 450). For consistency with other generated members and to exclude this generated code from code coverage metrics, this attribute should be added to the WhenAnyValue observable.
| $$""" | ||
| /// <summary> | ||
| /// Gets an observable for when the <see cref="{{propertyInfo.PropertyName}}"/> property | ||
| /// changes. | ||
| /// </summary> | ||
| {{propertyInfo.WhenAnyValueAccessModifier}} global::System.IObservable<{{propertyInfo.TypeNameWithNullabilityAnnotations}}> WhenAny{{propertyInfo.PropertyName}} => | ||
| this.WhenAnyValue(x => x.{{propertyInfo.PropertyName}}); | ||
| """; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generated WhenAnyValue observable property is missing the [GeneratedCode] attribute that is present on the generated reactive properties (see lines 468 and 486). For consistency and to follow best practices for generated code, the WhenAnyValue observable should also include this attribute to indicate it was automatically generated.
What kind of change does this PR introduce?
Feature
What is the new behavior?
Introduces WhenAnyValue and WhenAnyValueAccessModifier options to the [Reactive] attribute, enabling automatic generation of observable properties for value changes.
Updates documentation and test baselines to reflect new usage and API surface.
What might this PR break?
New feature
Please check if the PR fulfills these requirements
Other information: