Skip to content
Development
Skill

/r3-reactive-extensions

Build reactive/event-driven C# with R3 (Cysharp's modern reimplementation of Reactive Extensions). Covers the Observable<T>/Observer<T> model, the OnErrorResume error contract, async dispatch with AwaitOperation, Task/IAsyncEnumerable integration, TimeProvider/FrameProvider

From plugin
dotnet-skills
1.1k36 skills6 agents
Install
$ npx -y skills add aaronontheweb/dotnet-skills --skill r3-reactive-extensions --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/r3-reactive-extensions

Context preview

The summary Claude sees to decide when to auto-load this skill.

Build reactive/event-driven C# with R3 (Cysharp's modern reimplementation of Reactive Extensions). Covers the Observable<T>/Observer<T> model, the OnErrorResume error contract, async dispatch with AwaitOperation, Task/IAsyncEnumerable integration, TimeProvider/FrameProvider

SKILL.md

r3-reactive-extensions.SKILL.md
name: r3-reactive-extensions
description: Build reactive/event-driven C# with R3 (Cysharp's modern reimplementation of Reactive Extensions). Covers the Observable<T>/Observer<T> model, the OnErrorResume error contract, async dispatch with AwaitOperation, Task/IAsyncEnumerable integration, TimeProvider/FrameProvider scheduling, the concurrency contract, and how R3 differs from System.Reactive (Rx.NET).
invocable: false

R3: Modern Reactive Extensions for .NET

R3 is [Cysharp's](https://github.com/Cysharp/R3) ground-up reimplementation of Reactive Extensions — "the new future of dotnet/reactive and UniRx." It keeps the LINQ-over-events programming model but rebuilds the core types, error contract, and scheduler to fix long-standing problems in `System.Reactive` (Rx.NET). Use this skill when composing event streams, UI input, timers, or push-based pipelines in C#.

**Canonical sources** (link to these from code and docs):

  • Repository: https://github.com/Cysharp/R3
  • README (full operator reference): https://github.com/Cysharp/R3/blob/main/README.md
  • Author's design rationale: https://neuecc.medium.com/r3-a-new-modern-reimplementation-of-reactive-extensions-for-c-cf29abcc5826

When to Use This Skill

Use this skill when:

  • Composing **events** over time — UI input, sensor/feed updates, websocket messages, domain events
  • You need operators like debounce, throttle, merge, combine-latest, distinct-until-changed
  • Building **MVVM** state with `ReactiveProperty` / `BindableReactiveProperty`
  • Bridging push-based streams with `Task` / `async` and `IAsyncEnumerable`
  • Migrating from `System.Reactive`, UniRx, or `IObservable<T>` code
  • You hit Rx pain points: subscriptions dying on exceptions, scheduler overhead, or leak hunting

**Not the right tool for:** request/response I/O (use `async/await`), bounded producer/consumer with **backpressure** (use `System.Threading.Channels`), or server-side stream processing with batching/backpressure (use Akka.NET Streams). R3, like all Rx, is **push-based with no backpressure**. See the `csharp-concurrency-patterns` skill for choosing between these.

Reference Files

  • [rx-net-differences.md](rx-net-differences.md): Every meaningful difference vs System.Reactive (Rx.NET) — the new core types, the error model, operator renames, dropped APIs, the scheduler swap, and a migration checklist.
  • [async-and-integration-patterns.md](async-and-integration-patterns.md): Common patterns — async dispatch with `AwaitOperation`, `Task` integration, `IAsyncEnumerable` round-tripping, `ReactiveProperty`/MVVM, subjects, and subscription lifecycle.
  • [scheduling-and-concurrency.md](scheduling-and-concurrency.md): How R3 handles **concurrent updates** (the threading contract, `Synchronize`, `ObserveOn`), `TimeProvider` vs `FrameProvider`, when each is necessary, and deterministic testing with fake providers.

> Everything in this skill was validated empirically against **R3 1.3.1**. Captured output > appears in the reference files as evidence.

---

Why R3 Exists (the "why use it")

The author ([neuecc](https://neuecc.medium.com/r3-a-new-modern-reimplementation-of-reactive-extensions-for-c-cf29abcc5826)) built R3 to fix concrete defects in `System.Reactive`:

1. **Exceptions silently kill subscriptions.** In Rx, one exception in the pipeline calls `OnError` and *unsubscribes forever* — "a billion-dollar mistake" for long-lived event streams (a single bad UI event tears down the whole subscription). R3 routes errors to `OnErrorResume` and **keeps the subscription alive by default**. 2. **`IScheduler` is heavy and confusing.** `ImmediateScheduler`/`Merge` were measured causing real server memory/CPU bloat. R3 deletes `IScheduler` and uses .NET 8's `TimeProvider` (wall-clock) plus a new `FrameProvider` (frame-clock). 3. **Subscription leaks are hard to find.** R3 makes every `Observable<T>` an abstract class so all subscriptions funnel through one place, enabling `ObservableTracker` to list every live subscription with stack traces. 4. **Rx and async were awkwardly fused.** R3 treats Rx as **event-first** and adds explicit bridges (`AwaitOperation`, `FromAsync`, `ToAsyncEnumerable`) instead of pretending events are pull-based sequences. 5. **One library, every UI.** A platform-neutral core plus thin provider packages for Unity, Godot, WPF, WinForms, Avalonia, WinUI3, MAUI, Stride, MonoGame, and Blazor.

---

Install

dotnet add package R3
# Platform glue (pick what applies): R3.WPF, R3.Avalonia, R3.WinForms, R3.Unity (UPM),
# R3.Godot, ObservableCollections.R3, etc. See the repo README for the full list.
using R3;

---

The Mental Model

R3 replaces Rx's **interfaces** with **abstract classes**, and replaces Rx's two-method error contract with a single completion that carries a result.

public abstract class Observable<T>
{
    public IDisposable Subscribe(Observer<T> observer);     // tracked centrally
    protected abstract IDisposable SubscribeCore(Observer<T> observer);
}

public abstract class Observer<T> : IDisposable               // the observer IS the subscription
{
    public void OnNext(T value);
    public void OnErrorResume(Exception error);               // error WITHOUT unsubscribing
    public void OnCompleted(Result result);                   // success OR failure terminates
}

The grammar is `(OnNext | OnErrorResume)* OnCompleted(Result)?`. Note the difference from Rx's `OnNext* (OnError | OnCompleted)?`: **errors and termination are decoupled**. An error is just a notification; only `OnCompleted` ends the stream, and it carries a `Result` that is either `Result.Success` or `Result.Failure(exception)`.

Quick start

using R3;

var subscription = Observable
    .EveryValueChanged(model, m => m.SearchText)   // emits when the property changes
    .Debounce(TimeSpan.FromMilliseconds(300))      // Rx called this "Throttle" (see differences)
    .DistinctUnti
Read more
Ships withdotnet-skills

A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.

Get the whole plugin