Skip to content

feat: add ToProperty, ObservableAsPropertyHelper and [ObservableAsProperty] - #119

Merged
glennawatson merged 1 commit into
mainfrom
glennawatson/toproperty
Sep 24, 2026
Merged

glennawatson merged 1 commit into
mainfrom
glennawatson/toproperty

Conversation

@glennawatson

Copy link
Copy Markdown
Contributor

Summary

Adds ToProperty, which backs a read-only property with the latest value of an observable. The generator writes the code that raises the property's change notification, so each new value allocates nothing and no types are added to the consumer's assembly.

  • ObservableAsPropertyHelper<T> holds the latest value and raises the notification for each new one, in order, on the producing thread or a given scheduler. It supports deferred subscription, an initial value or factory, ThrownExceptions, and Default for a property that never changes.
  • ToProperty overloads take the property as a selector or a constant name, with an initial value or factory, deferred subscription, a scheduler, and an out parameter for the helper.
  • The generator raises through a raise method the type exposes, or adds one small accessor to a partial type that invokes its own event.
  • [ObservableAsProperty] on a partial get-only property generates the property body and its helper field.
  • ToPropertyUnsafe is the reflection fallback for a call the generator cannot read, such as a name held in a variable. It carries trimming and dynamic-code annotations.
  • New analyzer diagnostics: RXUIBIND012 when no notification can be raised, RXUIBIND013 when the property is not named directly, and RXUIBIND014 (error) when a positional string initial value is ambiguous below C# 13.
  • Pre-C# 10 file-and-line dispatch now matches Windows paths and chained calls split across lines, for every binding API.
  • The benchmarks are configured entirely by the configs each host passes, add a .NET Framework 4.6.2 job on Windows, and cover the new APIs.
  • The examples gain a runnable page for every new API and platform verifiers for .NET Framework, .NET 8 to 11, WPF, WinForms and MAUI. The System.Reactive examples are replaced by a README on using the System.Reactive package.

Why

Projects using this generator could not add another source generator to back properties with observables, so they had no way to do it with bindings. Running the new verifiers on .NET Framework on Windows also showed that pre-C# 10 dispatch never matched a Windows path or a multi-line chained call, so those calls threw at run time.

Breaking changes

None.

How this was verified

Tests cover the helper, the generator, the analyzers and the reflection fallback. The platform verifiers ran on Linux and on a Windows machine, for .NET Framework 4.6.2 through .NET 11, WPF, WinForms and MAUI. Apple platforms could not be run here.

Notes for the reviewer

  • Start with ObservableAsPropertyHelper.cs, then the ToProperty extractor and code generator. The subtle parts are the helper's gate and serialized delivery, and the dispatch rules for string names and OverloadResolutionPriority.
  • RuntimePropertyRaiser suppresses SES1406 on its two lookups that must reach non-public members; this was agreed as the one allowed use.
  • Mechanical and safe to skim: the generated ToProperty and ToPropertyUnsafe stub files, the PublicAPI baselines, the verified snapshots, and the removal of attributes from the benchmark classes.
  • The documentation is in the website PR: docs(binding): add the ReactiveUI.Binding documentation section website#999

Checklist

  • I have read the Contribute guide
  • The PR title follows Conventional Commits
  • Tests cover this change, or the summary says why they do not
  • New or changed public API has XML documentation

…perty]

Back a read-only property with the latest value of an observable, through
generated code that allocates nothing per value and adds no types to the
consumer's assembly.

Runtime
- ObservableAsPropertyHelper<T>: private core, serialized delivery, a
  distinct gate seeded with the initial value, ThrownExceptions, deferred
  subscription and Default. The gate is an int taken with one
  compare-exchange and a SpinWait backoff that never sleeps on .NET Core.
- ToProperty overloads for a selector or a property name, with an initial
  value or factory, deferred subscription, a scheduler, and an out helper.
  Selectors are Func, string names dispatch by value, and initial-value
  overloads carry OverloadResolutionPriority.
- ToPropertyUnsafe: the reflection fallback, which finds the raise member
  at run time and binds it once per helper.
- [ObservableAsProperty] for partial get-only properties.

Generator and analyzer
- ToProperty pipeline with raise plugins (a raise method, or an accessor
  added to a partial type) and a syntax-first extractor.
- [ObservableAsProperty] generator via ForAttributeWithMetadataName.
- File-and-line dispatch now matches Windows paths and the member-name
  line of chained calls.
- RXUIBIND012, RXUIBIND013 and RXUIBIND014.

Benchmarks and examples
- ToProperty, contention, ToPropertyUnsafe, extraction and discovery
  benchmarks; benchmarks are configured by config only, with a net462
  job on Windows.
- Documentation examples and platform verifiers for net462-net11.0, WPF,
  WinForms and MAUI; System.Reactive examples removed.
@glennawatson
glennawatson enabled auto-merge (squash) September 24, 2026 15:13
@glennawatson
glennawatson merged commit 61f234f into main Sep 24, 2026
19 checks passed
@glennawatson
glennawatson deleted the glennawatson/toproperty branch September 24, 2026 15:33
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
4.7% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@codecov

codecov Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.38290% with 108 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.05%. Comparing base (b842759) to head (1a9845c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...s/ReactiveUIBindingExtensions.ToProperty.Unsafe.cs 41.25% 47 Missing ⚠️
...nerators/CodeGeneration/ToPropertyCodeGenerator.cs 94.36% 15 Missing ⚠️
...ding.Shared/Bindings/ObservableAsPropertyHelper.cs 92.85% 12 Missing ⚠️
...I.Binding.Shared/Fallback/RuntimePropertyRaiser.cs 90.14% 7 Missing ⚠️
...ng.SourceGenerators/Helpers/ToPropertyExtractor.cs 92.30% 7 Missing ⚠️
...ceGenerators/Plugins/PropertyRaise/RaiseMembers.cs 89.18% 4 Missing ⚠️
...I.Binding.Analyzer/Analyzers/ToPropertyAnalyzer.cs 93.33% 3 Missing ⚠️
...alyzer/Analyzers/ToPropertyInitialValueAnalyzer.cs 92.10% 3 Missing ⚠️
...rs/Plugins/PropertyRaise/PartialTypeRaisePlugin.cs 94.73% 3 Missing ⚠️
...Plugins/PropertyRaise/ReactiveObjectRaisePlugin.cs 92.10% 3 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #119      +/-   ##
==========================================
- Coverage   98.05%   97.05%   -1.00%     
==========================================
  Files         349      368      +19     
  Lines       12731    13876    +1145     
  Branches     1539     1774     +235     
==========================================
+ Hits        12483    13468     +985     
- Misses        248      408     +160     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Oct 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant