ReactiveUI.Primitives.Async 7.5.0

Prefix Reserved
dotnet add package ReactiveUI.Primitives.Async --version 7.5.0
                    
NuGet\Install-Package ReactiveUI.Primitives.Async -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.Async" 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.Async" Version="7.5.0" />
                    
Directory.Packages.props
<PackageReference Include="ReactiveUI.Primitives.Async" />
                    
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.Async --version 7.5.0
                    
#r "nuget: ReactiveUI.Primitives.Async, 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.Async@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.Async&version=7.5.0
                    
Install as a Cake Addin
#tool nuget:?package=ReactiveUI.Primitives.Async&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.