Skip to content

feat(advanced): add the SwitchMap fused sink - #163

Merged
glennawatson merged 1 commit into
mainfrom
feat/switch-map-fused-sink
Aug 15, 2026
Merged

glennawatson merged 1 commit into
mainfrom
feat/switch-map-fused-sink

Conversation

@glennawatson

@glennawatson glennawatson commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Feature, plus a refactor of an existing operator onto the new sink.

What is the new behavior?

SwitchMap projects each value to an inner observable and mirrors the latest one, in a single fused sink.

  • SwitchMapSignal<TSource, TResult> is public in Advanced. One object and one observer hop, where a projection followed by a separate switch costs two of each plus an intermediate sequence of observables.
  • Every source value switches, null included. A caller that projects null onto its own inner observable switches onto it, which detaches the previous inner and drops its subscription.
  • SwitchSelect is reimplemented on the same sink. It keeps its existing behavior of skipping nulls, which leaves the active inner subscription in place. The two now differ only in that flag; the switching bookkeeping - generation ids, the gate, the outer/inner completion interlock - exists once instead of twice.
  • Both flavours get it. The sink is shared source, so it lands in ReactiveUI.Primitives.Advanced and ReactiveUI.Primitives.Reactive.Advanced.

What is the current behavior?

Reaching the latest inner observable from a projection means composing two sinks.

  • Select(selector).Switch() allocates the projection sink, its observer, the switch sink and its observer, and threads an IObservable<IObservable<T>> between them.
  • SwitchSelect is the only fused form and it always skips nulls, so it cannot express a projection that switches on null.

What might this PR break?

None.

  • SwitchMap is new API.
  • SwitchSelect keeps its signature and its skip-null semantics; only its implementation moved.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

Most of the diff is the public API baselines; two files were authored.

  • src/Primitives.Shared/Advanced/SwitchMapSignal{TSource,TResult}.cs - the sink.
  • src/Primitives.Shared/SignalOperatorMixins.ChooseSwitchSelect.cs - the SwitchMap operator, and SwitchSelect reduced to a call into the sink.
  • src/tests/ReactiveUI.Primitives.Tests/SwitchMapTests.cs - covers switching on null, stale-inner suppression, the completion interlock, selector/outer/inner errors, and disposal.
  • The remaining 36 files are one added block each in PublicAPI.Shipped.txt, inserted at its sorted position.

The existing SwitchSelect tests are unchanged and still pass, which is what pins the refactor.

- Add SwitchMapSignal, which projects each source value to an inner
  observable and mirrors the latest one in a single sink, and the
  SwitchMap operator over it.
- Every source value switches, null included, so a caller that projects
  null onto its own inner observable detaches the previous one.
- Reimplement SwitchSelect on the same sink, which skips nulls instead
  of switching on them. Its switching bookkeeping now lives in one place.
@glennawatson
glennawatson force-pushed the feat/switch-map-fused-sink branch from e875c46 to d98172f Compare August 15, 2026 08:17
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Aug 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.14%. Comparing base (8dbb7e6) to head (d98172f).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #163      +/-   ##
==========================================
- Coverage   98.14%   98.14%   -0.01%     
==========================================
  Files         679      680       +1     
  Lines       21417    21432      +15     
  Branches     2619     2619              
==========================================
+ Hits        21020    21034      +14     
  Misses        197      197              
- Partials      200      201       +1     

☔ 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.

@glennawatson
glennawatson merged commit 420830d into main Aug 15, 2026
12 of 13 checks passed
@glennawatson
glennawatson deleted the feat/switch-map-fused-sink branch August 15, 2026 09:21
@github-actions

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 Aug 30, 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.

2 participants

⌂ 换网址