ReactiveUI.Primitives.Wpf 7.5.0

Prefix Reserved
dotnet add package ReactiveUI.Primitives.Wpf --version 7.5.0
                    
NuGet\Install-Package ReactiveUI.Primitives.Wpf -Version 7.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ReactiveUI.Primitives.Wpf" Version="7.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ReactiveUI.Primitives.Wpf" Version="7.5.0" />
                    
Directory.Packages.props
<PackageReference Include="ReactiveUI.Primitives.Wpf" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ReactiveUI.Primitives.Wpf --version 7.5.0
                    
#r "nuget: ReactiveUI.Primitives.Wpf, 7.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ReactiveUI.Primitives.Wpf@7.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ReactiveUI.Primitives.Wpf&version=7.5.0
                    
Install as a Cake Addin
#tool nuget:?package=ReactiveUI.Primitives.Wpf&version=7.5.0
                    
Install as a Cake Tool

NuGet Stats Build Code Coverage #yourfirstpr <br> <a href="https://www.nuget.org/packages/ReactiveUI.Primitives"> <img src="https://img.shields.io/nuget/dt/ReactiveUI.Primitives.svg"> </a> <a href="https://reactiveui.net/slack"> <img src="https://img.shields.io/badge/chat-slack-blue.svg"> </a>

<img alt="ReactiveUI.Primitives" width="160" height="160" src="https://github.com/reactiveui/styleguide/blob/master/logo_primitives/logo.png?raw=true">

ReactiveUI.Primitives

ReactiveUI.Primitives is a small, fast library for reactive programming in .NET. Reactive programming means working with values that arrive over time, such as button clicks, timer ticks, or network replies, rather than values you already hold.

If you know LINQ, you already know the shape. LINQ queries a collection you already hold and pulls values out of an IEnumerable<T>. Reactive programming queries values that arrive over time: an IObservable<T> pushes each value to you as it happens. The operators carry over, so Select, Where, and Aggregate keep their meaning here. This library also gives them the names Map, Keep, and Fold.

It gives you that model without a runtime dependency on System.Reactive, R3, or R3Async. Those are the established reactive libraries for .NET, and this package stands in for them in the common cases.

It builds on two interfaces that .NET already ships. IObservable<T> is a source you subscribe to. IObserver<T> is the subscriber that receives each value. The library renames a few common concepts for clarity. It also favours code paths that allocate little memory and run under ahead-of-time (AOT) compilation. AOT compiles the app to native code before it runs, so the app cannot generate new code while running.

Goals and design posture

ReactiveUI.Primitives aims to:

  • Cover the Rx model over IObservable<T>: creating streams, subscribing, holding state, scheduling work, and composing operators. A stream is a sequence of values delivered over time.
  • Rename a few concepts where a clearer name helps. A Signal<T> is a source you can both push values into and subscribe to (Rx calls this a Subject<T>). Map transforms each value (Rx Select); Keep filters values (Rx Where); Spark turns each notification into a value you can inspect.
  • Stay AOT-friendly. The production package uses no runtime reflection, no generated code, no expression compilation, and no hidden dependency on System.Reactive, R3, or R3Async.
  • Allocate as little as possible on hot paths. For example, Signal<T> subscribes a single delegate directly, and the common return, empty, and never sources reuse one shared instance.
  • Run in production across modern .NET and .NET Framework, with separate integration packages for Windows UI and other platforms. A target framework (TFM) is the .NET version and platform a build targets, such as net8.0.
  • Support migration. The .Reactive package variants match System.Reactive's public surface, and source-generator bridges connect to R3 or R3Async when your project already uses them.

Why not System.Reactive or R3?

System.Reactive is the original Rx library for .NET, and the reason IObservable<T> exists. It is mature and widely used. Its weak point is performance: a typical operator chain allocates several objects per operator and per value, and that grows under heavy load.

R3 is a newer library aimed at that weak point. It is fast. It reaches that speed partly by replacing IObservable<T> with its own Observable<T> type. That swap means existing code, and the wider ecosystem built on IObservable<T>, does not carry over without adaptation.

We wanted the speed without the break, so we kept IObservable<T>, the interface .NET already ships and most C# code already knows. Our benchmarks pointed at the cause: the interface was not the bottleneck. The cost lived in how the operators were implemented, not in the abstraction. So we kept the familiar contract and rebuilt the operators as low-allocation sinks (see Why the operators are built this way).

This keeps the change small for anyone already on IObservable<T>. You keep the contract and the mental model, and you gain the lower allocation profile. When you do need full System.Reactive or R3 behaviour, the .Reactive package variants and the R3/R3Async source-generator bridges cover those boundaries.

Where we could not stay on the standard types

Keeping IObservable<T> and IObserver<T> was easy, because both ship in .NET itself. Two related types do not, so we had to make a call.

The first is the scheduler. A scheduler decides when and on which thread work runs. .NET has no scheduler type of its own. The standard one, IScheduler, lives in System.Reactive, so using it would pull System.Reactive back in as a runtime dependency. That is the dependency we set out to avoid. So the lean library defines its own small scheduling contract, ISequencer.

The second is Unit. Unit is the type that means "a value carrying no information", used for streams that report that something happened but carry no data. .NET has no such type either, and the common Unit also lives in System.Reactive. So the lean library defines its own, RxVoid.

These two types are the only places the lean surface departs from the System.Reactive shape. The .Reactive package variants close the gap: they recompile the same source with ISequencer mapped to IScheduler and RxVoid mapped to System.Reactive.Unit, so code that already speaks System.Reactive sees the types it expects.

Disposal groups are a third seam, and one the shared types cannot close on their own: MultipleDisposable ships in the dependency-free ReactiveUI.Disposables package, so it cannot name CompositeDisposable. ReactiveUI.Primitives.Reactive adds ContainerDisposable for that - a MultipleDisposable that converts implicitly to a CompositeDisposable it owns and disposes. Hand one to DisposeWith, to a library that takes a CompositeDisposable, or to your own helper, and it just works; anything registered through the composite is disposed with the container.

Table of contents

  1. Install
  2. Agent Skills
  3. Target frameworks and dependencies
  4. Core model
  5. Creation factories
  6. Operators
  7. ReactiveUI.Primitives.Async
  8. Extension helpers
  9. Stateful signals and subject-like types
  10. Sequencers
  11. Threading, disposal, and error semantics
  12. Source-generator bridge behavior
  13. Migration guides
  14. Benchmarks and performance posture
  15. Repository layout

Install

All packages are published on NuGet.org. Install the base package:

dotnet add package ReactiveUI.Primitives

The library is split into a layered set of packages, so you can pull only the surface that matches your integration point. Every package below is produced by a packable project in the current solution and ships at the same version. Target frameworks vary by package; the exact matrices are documented under Target frameworks and dependencies.

Package NuGet Use when
ReactiveUI.Disposables DispB You only need the disposable primitives such as Disposable, MultipleDisposable, Slot, or Pocket.
ReactiveUI.Primitives.Core CoreB The type-agnostic core shared by the lean and System.Reactive-flavoured leaves (usually a transitive dependency).
ReactiveUI.Primitives PrimB The default lean signal/operator/sequencer package, including the migrated ReactiveUI.Extensions helpers.
ReactiveUI.Primitives.Reactive RxB The Primitives and extension-helper APIs compiled against System.Reactive Unit and IScheduler.
ReactiveUI.Primitives.Async.Core AsyncCoreB The type-agnostic async core shared by the async leaves.
ReactiveUI.Primitives.Async AsyncB Native IObservableAsync<T> / IObserverAsync<T> signals.
ReactiveUI.Primitives.ObservableEvents EventsB Optional analyzer package that exposes .NET events as provider-native IObservable<T> properties.
ReactiveUI.Primitives.R3Bridge.Generator R3BridgeB Optional analyzer package that generates R3 and R3Async bridge adapters.
ReactiveUI.Primitives.Async.Reactive AsyncRxB Async Primitives compiled against System.Reactive Unit and IScheduler.
ReactiveUI.Primitives.Wpf WpfB WPF dispatcher sequencer integration.
ReactiveUI.Primitives.Wpf.Reactive WpfRxB WPF dispatcher scheduler integration for System.Reactive-first projects.
ReactiveUI.Primitives.WinForms WinFormsB Windows Forms control sequencer integration.
ReactiveUI.Primitives.WinForms.Reactive WinFormsRxB Windows Forms control scheduler integration for System.Reactive-first projects.
ReactiveUI.Primitives.WinUI WinUIB WinUI dispatcher-queue sequencer integration.
ReactiveUI.Primitives.WinUI.Reactive WinUIRxB WinUI dispatcher-queue scheduler integration for System.Reactive-first projects.
ReactiveUI.Primitives.Blazor BlazorB Blazor renderer sequencer integration.
ReactiveUI.Primitives.Blazor.Reactive BlazorRxB Blazor renderer scheduler integration for System.Reactive-first projects.
ReactiveUI.Primitives.Avalonia AvaloniaB Avalonia UI-thread sequencer integration.
ReactiveUI.Primitives.Avalonia.Reactive AvaloniaRxB Avalonia UI-thread scheduler integration for System.Reactive-first projects.
ReactiveUI.Primitives.Maui MauiB MAUI dispatcher sequencer integration.
ReactiveUI.Primitives.Maui.Reactive MauiRxB MAUI dispatcher scheduler integration for System.Reactive-first projects.

How the packages layer

The base and async families use type-agnostic .Core projects, with a lean leaf binding the shared RxVoid/ISequencer source to lightweight implementations and a .Reactive leaf recompiling it against System.Reactive's Unit/IScheduler. Type-agnostic extension-helper sources are compiled into ReactiveUI.Primitives.Core, while the lean and System.Reactive helper surfaces ship from ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive. The src/ReactiveUI.Primitives.Extensions.Core directory is source only; it is not a project or NuGet package. The platform packages also come in lean and .Reactive leaves. (Arrows point from a package to what it depends on.)

graph TD
    SR["System.Reactive"]
    Disp["ReactiveUI.Disposables"]
    Core["ReactiveUI.Primitives.Core"]
    Prim["ReactiveUI.Primitives<br/>(lean)"]
    Rx["ReactiveUI.Primitives.Reactive"]
    AsyncCore["...Async.Core"]
    Async["...Async (lean)"]
    AsyncRx["...Async.Reactive"]
    Plat["Wpf / WinForms / WinUI / Blazor<br/>Avalonia / Maui"]
    PlatRx["Wpf.Reactive / WinForms.Reactive / WinUI.Reactive<br/>Blazor.Reactive / Avalonia.Reactive / Maui.Reactive"]

    Core --> Disp
    Prim --> Core
    Prim --> Disp
    Rx --> Core
    Rx --> SR
    AsyncCore --> Core
    Async --> Prim
    Async --> AsyncCore
    AsyncRx --> Rx
    AsyncRx --> AsyncCore
    Plat --> Prim
    PlatRx --> Rx

ReactiveUI.Primitives.Extensions and ReactiveUI.Primitives.Extensions.Reactive are no longer separate projects or NuGet packages. Their implementations now ship from ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive, respectively. No API code was removed: the former lean Extensions package already depended on ReactiveUI.Primitives, and the former Reactive Extensions package already depended on ReactiveUI.Primitives.Reactive. Replace only the package reference; the existing ReactiveUI.Primitives.Extensions* namespaces remain unchanged.

Then import the namespaces you need:

using ReactiveUI.Primitives;
using ReactiveUI.Primitives.Async;
using ReactiveUI.Primitives.Concurrency;
using ReactiveUI.Primitives.Disposables;
using ReactiveUI.Primitives.Extensions;
using ReactiveUI.Primitives.Extensions.Reactive;
using ReactiveUI.Primitives.Async.Signals;
using ReactiveUI.Primitives.Async.Reactive;
using ReactiveUI.Primitives.Reactive;
using ReactiveUI.Primitives.Signals;

The package metadata is configured to include this README in the NuGet package via PackageReadmeFile=README.md. The base package also packs Skill.md at the package root and a Codex-ready copy at .agents/skills/reactiveui-primitives/SKILL.md.

R3 and R3Async bridge generation lives in the standalone ReactiveUI.Primitives.R3Bridge.Generator analyzer package:

dotnet add package ReactiveUI.Primitives.R3Bridge.Generator

That generator does not add runtime R3 or R3Async dependencies to ReactiveUI.Primitives. It emits bridge code only when the consuming compilation already references the relevant external library symbols. System.Reactive interop is provided by the .Reactive package variants rather than by generated System.Reactive bridge methods.

Agent Skills

The base ReactiveUI.Primitives NuGet package includes Skill.md at the package root and a Codex-ready copy at .agents/skills/reactiveui-primitives/SKILL.md. It is an agent-oriented guide for choosing the correct ReactiveUI.Primitives package, using Async, extension helpers, UI sequencers, bridge source generators, and migration from System.Reactive package variants, R3, or R3Async while assuming the libraries are consumed from NuGet packages.

After package restore, locate the file in the local NuGet package cache:

$version = "<version>"
$skill = "$env:USERPROFILE\.nuget\packages\reactiveui.primitives\$version\.agents\skills\reactiveui-primitives\SKILL.md"

On macOS or Linux:

version="<version>"
skill="$HOME/.nuget/packages/reactiveui.primitives/$version/.agents/skills/reactiveui-primitives/SKILL.md"

Install or link the packaged SKILL.md into the instruction location supported by the agent. Skill.md remains at the package root for agents or tools that expect a singular markdown guide rather than a skill folder.

Agent Recommended project-local install Notes
OpenAI Codex .agents/skills/reactiveui-primitives/SKILL.md Codex also supports user-level skills under $HOME/.agents/skills.
Claude Code .claude/skills/reactiveui-primitives/SKILL.md Claude Code also supports personal skills under ~/.claude/skills.
Cline .cline/skills/reactiveui-primitives/SKILL.md Cline skills must be enabled in Cline's feature settings.
GitHub Copilot .github/instructions/reactiveui-primitives.instructions.md For repository-wide behavior, summarize or link the skill from .github/copilot-instructions.md.
Cursor .cursor/rules/reactiveui-primitives.mdc Cursor project rules are version-controlled under .cursor/rules; CLAUDE.md is authoritative in this repo, and AGENTS.md can point to it for compatibility.
Windsurf .windsurf/rules/reactiveui-primitives.md Windsurf can consume repository guidance via markdown rules; CLAUDE.md is the canonical file in this repo.
Gemini CLI GEMINI.md or an imported file referenced from GEMINI.md Gemini CLI loads hierarchical context files and supports importing other markdown files with @file.md.

Target frameworks and dependencies

Most shared library packages use $(LibraryTargetFrameworks) from src/Directory.Build.props and currently target:

  • net8.0
  • net9.0
  • net10.0
  • net11.0
  • net462
  • net472
  • net48
  • net481

Package TFM groups are:

  • ReactiveUI.Disposables, ReactiveUI.Primitives.Core, ReactiveUI.Primitives.Async.Core, ReactiveUI.Primitives.Async, and ReactiveUI.Primitives.Async.Reactive: $(LibraryTargetFrameworks).
  • ReactiveUI.Primitives.ObservableEvents and ReactiveUI.Primitives.R3Bridge.Generator: netstandard2.0.
  • ReactiveUI.Primitives: $(LibraryTargetFrameworks) plus net10.0-android, net11.0-android, and Apple platform TFMs (net10.0-ios, net11.0-ios, net10.0-tvos, net11.0-tvos, net10.0-macos, net11.0-macos, net10.0-maccatalyst, net11.0-maccatalyst) when building on Windows or macOS.
  • ReactiveUI.Primitives.Reactive: the same matrix as ReactiveUI.Primitives, compiled with System.Reactive Unit and IScheduler aliases.
  • ReactiveUI.Primitives.Wpf and ReactiveUI.Primitives.Wpf.Reactive: net8.0-windows, net9.0-windows, net10.0-windows, net11.0-windows, net462, net472, net48, net481.
  • ReactiveUI.Primitives.WinForms and ReactiveUI.Primitives.WinForms.Reactive: net8.0-windows, net9.0-windows, net10.0-windows, net11.0-windows, net462, net472, net48, net481.
  • ReactiveUI.Primitives.WinUI and ReactiveUI.Primitives.WinUI.Reactive: net8.0-windows10.0.19041.0, net9.0-windows10.0.19041.0, net10.0-windows10.0.19041.0, net11.0-windows10.0.19041.0.
  • ReactiveUI.Primitives.Blazor and ReactiveUI.Primitives.Blazor.Reactive: net8.0, net9.0, net10.0, net11.0.
  • ReactiveUI.Primitives.Avalonia and ReactiveUI.Primitives.Avalonia.Reactive: net8.0, net9.0, net10.0, net11.0.
  • ReactiveUI.Primitives.Maui and ReactiveUI.Primitives.Maui.Reactive: net10.0, net11.0.

Runtime package dependencies are intentionally small. The default production packages do not depend on System.Reactive, R3, R3Async, or the optional R3 bridge generator. ReactiveUI.Primitives references ReactiveUI.Disposables, and ReactiveUI.Primitives.Core. ReactiveUI.Primitives.Core contains the type-agnostic implementation used by the extension-helper surfaces. ReactiveUI.Disposables references System.ValueTuple only for net462.

The .Reactive leaf packages intentionally reference System.Reactive through src/Directory.Build.props. They recompile the shared Primitives source with RxVoid aliased to System.Reactive.Unit, ISequencer aliased to System.Reactive.Concurrency.IScheduler, and the shared source shifted into .Reactive namespaces.

ReactiveUI.Primitives, ReactiveUI.Primitives.Reactive, ReactiveUI.Primitives.Async.Core, ReactiveUI.Primitives.Async, and ReactiveUI.Primitives.Async.Reactive add .NET Framework compatibility/support packages where required, such as System.ValueTuple, Microsoft.Bcl.TimeProvider, System.Threading.Channels, System.Runtime.CompilerServices.Unsafe, System.ComponentModel.Annotations, System.Buffers, System.Memory, and System.Collections.Immutable. Add the standalone ReactiveUI.Primitives.R3Bridge.Generator analyzer package to generate R3/R3Async bridge methods in consuming projects that already reference those external libraries.

ReactiveUI.Primitives.Blazor and ReactiveUI.Primitives.Blazor.Reactive reference Microsoft.AspNetCore.Components. ReactiveUI.Primitives.Avalonia and ReactiveUI.Primitives.Avalonia.Reactive reference Avalonia. ReactiveUI.Primitives.Maui and ReactiveUI.Primitives.Maui.Reactive reference Microsoft.Maui.Core and Microsoft.Extensions infrastructure packages. ReactiveUI.Primitives.WinUI and ReactiveUI.Primitives.WinUI.Reactive reference Microsoft.WindowsAppSDK. The remaining shared package references are analyzer, SourceLink, versioning, ILLink, reference-assembly, or build-time support packages such as Blazor.Common.Analyzers, Microsoft.SourceLink.GitHub, MinVer, Roslynator.Analyzers, SonarAnalyzer.CSharp, StyleSharp.Analyzers, Microsoft.NET.ILLink.Tasks, and Microsoft.NETFramework.ReferenceAssemblies. Benchmark projects may reference System.Reactive, System.Reactive.Async 6.0.0-alpha.18, R3, and ReactiveUI.Extensions as comparison baselines, but those references are not production dependencies.

Core model

Signal<T>

Signal<T> is the basic signal type: a source you can both push values into and subscribe to. It implements ISignal<T>, which combines IObserver<T>, IObservable<T>, and IsDisposed.

Use it when code needs to push values into a stream and let observers subscribe:

using ReactiveUI.Primitives;
using ReactiveUI.Primitives.Signals;

var signal = new Signal<int>();

using IDisposable subscription = signal.Subscribe(
    value => Console.WriteLine($"next: {value}"),
    error => Console.WriteLine($"error: {error.Message}"),
    () => Console.WriteLine("completed"));

signal.OnNext(1);
signal.OnNext(2);
signal.OnCompleted();

Important behavior:

  • OnNext(T) sends a value to active subscribers.
  • OnError(Exception) terminates the signal with an error.
  • OnCompleted() terminates the signal successfully.
  • Subscribe(...) returns IDisposable; disposing the subscription unsubscribes.
  • HasObservers and IsDisposed expose basic lifecycle state.
  • The Subscribe(Action<T>) extension uses an optimized direct-action path for Signal<T> when possible.

Observers and witnesses

ReactiveUI.Primitives keeps the standard IObserver<T> shape and provides helper observer implementations internally under the Core namespace.

Common user-facing subscription overloads live in SubscribeExtensions:

using ReactiveUI.Primitives;
using ReactiveUI.Primitives.Signals;

var signal = new Signal<string>();

using var nextOnly = signal.Subscribe(value => Console.WriteLine(value));
using var full = signal.Subscribe(
    value => Console.WriteLine(value),
    error => Console.Error.WriteLine(error),
    () => Console.WriteLine("done"));

The library uses the term witness for lightweight observer wrappers. You normally use delegates or IObserver<T> directly rather than constructing witness types by hand.

Using Primitives alongside System.Reactive

Packages such as DynamicData can bring in System.Reactive transitively. Importing both System and ReactiveUI.Primitives then exposes two sets of Subscribe extension methods for IObservable<T>. Use SubscribePrimitives to select the Primitives implementation without changing the observable:

using var subscription = saveCommand.ThrownExceptions.SubscribePrimitives(
    error => activity.AddItem(error.ToString()));

It has the same five callback overloads and behavior as Subscribe, including disposal and unhandled-error propagation. Existing Subscribe APIs remain available. An explicit static call also selects Primitives:

using var subscription = SubscribeExtensions.Subscribe(
    saveCommand.ThrownExceptions,
    error => activity.AddItem(error.ToString()));

Putting using ReactiveUI.Primitives; inside the consuming namespace also gives its extension methods precedence over a global using System;. This applies per namespace; a global import alone does not resolve the conflict. Import only one set of LINQ operators when their signatures overlap.

SubscribeSafe is not a drop-in rename: its single Action<Exception> overload handles terminal errors, not values emitted by an IObservable<Exception>. To handle exception values with SubscribeSafe, supply both onNext and onError explicitly.

System.Reactive declares its own observer-taking SubscribeSafe in the System namespace, so that one overload is ambiguous under the same conditions as Subscribe. Use SubscribeSafePrimitives(observer) to select the Primitives implementation. The callback shapes of SubscribeSafe have no System.Reactive counterpart and stay callable under their own name.

Scheduling event handlers and drawing

ObserveOn schedules downstream notifications. Moving work from an event handler into a subscriber after ObserveOn therefore changes when that work runs, even when the scheduler targets the UI thread. For paint events such as SkiaSharp's PaintSurface, draw synchronously while the event's surface is valid. Do not defer use of its canvas through ObserveOn or an await. Schedule a redraw request instead, and perform the drawing in the resulting paint callback.

The Signal.FromEventPattern<TEventHandler, TEventArgs>(conversion, addHandler, removeHandler) overload lets a custom event handler perform synchronous work before invoking the notification callback. Each subscription owns its converted handler and detaches that same handler on disposal. Supplying the conversion also avoids deriving the handler reflectively, which is what makes this shape trim- and AOT-safe.

Three siblings build on the same conversion. FromEventPattern<TEventHandler, TSender, TEventArgs> keeps the sender's static type instead of erasing it to object. FromEvent<TEventHandler, TEventArgs> emits the event argument on its own, for events that carry no sender, and FromEvent<TEventArgs>(addHandler, removeHandler) covers the plain Action<TEventArgs> case. Every one of them, and every FromEventPattern overload, accepts a trailing sequencer that attaches and detaches the handler as scheduled work rather than on the subscribing thread — the shape to use when an event may only be subscribed from the UI thread:

using var painted = Signal.FromEventPattern<SKPaintSurfaceEventArgs>(
        handler => view.SkiaElement.PaintSurface += handler,
        handler => view.SkiaElement.PaintSurface -= handler,
        RxSchedulers.MainThreadScheduler)
    .SubscribePrimitives(pattern => Draw(pattern.EventArgs));

Disposing cancels a pending attach, so a subscription torn down before the sequencer ran it never leaves the handler on the event.

Throttle (also called Calm or Stabilize) waits for a quiet period after the most recent value. By default its timer uses the thread pool; it does not marshal the result to the UI thread. Use Throttle(duration, uiSequencer) or put ObserveOn(uiSequencer) after Throttle when the subscriber requires the UI thread. Source completion flushes a pending value immediately, matching Rx debounce semantics; it does not wait for the remaining quiet period.

Disposables, handles, and slots

Subscriptions and scheduled work return IDisposable. ReactiveUI.Primitives includes lightweight disposable primitives in ReactiveUI.Primitives.Disposables:

Type Use
Disposable.Create(Action) Create an IDisposable from a cleanup action.
Disposable.Empty No-op disposable.
BooleanDisposable Track simple disposed state.
CancellationDisposable Tie disposal to a CancellationTokenSource.
MultipleDisposable Composite-disposable equivalent; add/remove multiple disposables.
CompositeDisposable System.Reactive-compatible alias over MultipleDisposable.
Pocket Named MultipleDisposable specialization.
SingleDisposable / AssignmentSlot Single-assignment disposable container.
SingleReplaceableDisposable / Slot Replaceable disposable container.
Handle, Handle<T>, Handle<T1,T2>, Handle<T1,T2,T3> Lightweight handle wrappers for resource lifetimes.

Example:

using ReactiveUI.Primitives;
using ReactiveUI.Primitives.Disposables;
using ReactiveUI.Primitives.Signals;

var subscriptions = new MultipleDisposable();
var signal = new Signal<int>();

signal.Subscribe(value => Console.WriteLine(value)).DisposeWith(subscriptions);
signal.Subscribe(value => Console.WriteLine(value * 10)).DisposeWith(subscriptions);

signal.OnNext(3);
subscriptions.Dispose();

Creation factories

Creation APIs live on ReactiveUI.Primitives.Signals.Signal.

Factory Purpose
Signal.Create<T>(Func<IObserver<T>, IDisposable>) Build a custom observable.
Signal.CreateSafe<T>(Func<IObserver<T>, IDisposable>) Build a custom observable with safety wrapping.
Signal.CreateWithState<T,TState>(...) Build a custom observable while passing state explicitly.
Signal.Lazy<T>(Func<IObservable<T>>) Create the source per subscription.
Signal.Emit<T>(T) Emit one value and complete. Specialized fast paths exist for bool, int, and RxVoid.
Signal.None<T>() Complete without values.
Signal.Silent<T>() / Signal.Silent<T>(T witness) Never emit and never complete.
Signal.Fail<T>(Exception) Terminate with an error.
Signal.Sequence(int start, int count)