{"id":44363301,"url":"https://github.com/Cysharp/R3","last_synced_at":"2026-02-14T12:01:12.706Z","repository":{"id":216184111,"uuid":"728573769","full_name":"Cysharp/R3","owner":"Cysharp","description":"The new future of dotnet/reactive and UniRx.","archived":false,"fork":false,"pushed_at":"2025-10-01T07:20:20.000Z","size":2723,"stargazers_count":3511,"open_issues_count":31,"forks_count":150,"subscribers_count":49,"default_branch":"main","last_synced_at":"2026-01-07T09:15:05.412Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cysharp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["neuecc"]}},"created_at":"2023-12-07T08:28:22.000Z","updated_at":"2026-01-07T02:44:45.000Z","dependencies_parsed_at":"2024-04-30T05:11:46.843Z","dependency_job_id":"9bf113d7-e489-4b6a-9fc3-9af0279e09e2","html_url":"https://github.com/Cysharp/R3","commit_stats":null,"previous_names":["cysharp/r3"],"tags_count":57,"template":false,"template_full_name":null,"purl":"pkg:github/Cysharp/R3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FR3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FR3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FR3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FR3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cysharp","download_url":"https://codeload.github.com/Cysharp/R3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FR3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29443468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T10:51:12.367Z","status":"ssl_error","status_checked_at":"2026-02-14T10:50:52.088Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-11T18:00:27.510Z","updated_at":"2026-02-14T12:01:12.689Z","avatar_url":"https://github.com/Cysharp.png","language":"C#","funding_links":["https://github.com/sponsors/neuecc"],"categories":["C# #","Open Source Repositories"],"sub_categories":["Utilities"],"readme":"# R3\n\nThe new future of [dotnet/reactive](https://github.com/dotnet/reactive/) and [UniRx](https://github.com/neuecc/UniRx), which support many platforms including [Unity](#unity), [Godot](#godot), [Avalonia](#avalonia), [WPF](#wpf), [WinForms](#winforms), [WinUI3](#winui3), [Stride](#stride), [LogicLooper](#logiclooper), [MAUI](#maui), [MonoGame](#monogame), [Blazor](#blazor), [Uno](#uno).\n\nI have over 10 years of experience with Rx, experience in implementing a custom Rx runtime ([UniRx](https://github.com/neuecc/UniRx)) for game engine, and experience in implementing an asynchronous runtime ([UniTask](https://github.com/Cysharp/UniTask/)) for game engine. Based on those experiences, I came to believe that there is a need to implement a new Reactive Extensions for .NET, one that reflects modern C# and returns to the core values of Rx.\n\n* Stopping the pipeline at OnError is a mistake.\n* IScheduler is the root of poor performance.\n* Frame-based operations, a missing feature in Rx, are especially important in game engines.\n* Single asynchronous operations should be entirely left to async/await.\n* Synchronous APIs should not be implemented.\n* Query syntax is a bad notation except for SQL.\n* The Necessity of a subscription list to prevent subscription leaks (similar to a Parallel Debugger)\n* Backpressure should be left to [IAsyncEnumerable](https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/generate-consume-asynchronous-stream) and [Channels](https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/).\n* For distributed processing and queries, there are [GraphQL](https://graphql.org/), [Kubernetes](https://kubernetes.io/), [Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/), [Akka.NET](https://getakka.net/), [gRPC](https://grpc.io/), [MagicOnion](https://github.com/Cysharp/MagicOnion).\n\nIn other words, LINQ is not for EveryThing, and we believe that the essence of Rx lies in the processing of in-memory messaging (LINQ to Events), which will be our focus. We are not concerned with communication processes like [Reactive Streams](https://www.reactive-streams.org/).\n\nTo address the shortcomings of dotnet/reactive, we have made changes to the core interfaces. In recent years, Rx-like frameworks optimized for language features, such as [Kotlin Flow](https://kotlinlang.org/docs/flow.html) and [Swift Combine](https://developer.apple.com/documentation/combine), have been standardized. C# has also evolved significantly, now at C# 12, and we believe there is a need for an Rx that aligns with the latest C#.\n\nImproving performance was also a theme in the reimplementation. For example, this is the result of the terrible performance of IScheduler and the performance difference caused by its removal.\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/68a12664-a840-4725-a87c-8fdbb03b4a02)\n`Observable.Range(1, 10000).Subscribe()`\n\nYou can also see interesting results in allocations with the addition and deletion to Subject.\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/2194c086-37a3-44d6-8642-5fd0fa91b168)\n`x10000 subject.Subscribe() -\u003e x10000 subscription.Dispose()`\n\nThis is because dotnet/reactive has adopted ImmutableArray (or its equivalent) for Subject, which results in the allocation of a new array every time one is added or removed. Depending on the design of the application, a large number of subscriptions can occur (we have seen this especially in the complexity of games), which can be a critical issue. In R3, we have devised a way to achieve high performance while avoiding ImmutableArray.\n\nFor those interested in learning more about the implementation philosophy and comparisons, please refer to my blog article [R3 — A New Modern Reimplementation of Reactive Extensions for C#](https://neuecc.medium.com/r3-a-new-modern-reimplementation-of-reactive-extensions-for-c-cf29abcc5826).\n\nCore Interface\n---\nThis library is distributed via [NuGet packages/R3](https://www.nuget.org/packages/R3), supporting .NET Standard 2.0, .NET Standard 2.1, .NET 6(.NET 7) and .NET 8 or above.\n\n```bash\ndotnet add package R3\n```\n\nSome platforms(WPF, Avalonia, Unity, Godot, etc...) requires additional step to install. Please see [Platform Supports](#platform-supports) section in below.\n\nR3 code is mostly the same as standard Rx. Make the Observable via factory methods(Timer, Interval, FromEvent, Subject, etc...) and chain operator via LINQ methods. Therefore, your knowledge about Rx and documentation on Rx can be almost directly applied. If you are new to Rx, the [ReactiveX](https://reactivex.io/intro.html) website and [Introduction to Rx.NET](https://introtorx.com/) would be useful resources for reference.\n\n```csharp\nusing R3;\n\nvar subscription = Observable.Interval(TimeSpan.FromSeconds(1))\n    .Select((_, i) =\u003e i)\n    .Where(x =\u003e x % 2 == 0)\n    .Subscribe(x =\u003e Console.WriteLine($\"Interval:{x}\"));\n\nvar cts = new CancellationTokenSource();\n_ = Task.Run(() =\u003e { Console.ReadLine(); cts.Cancel(); });\n\nawait Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3))\n    .TakeUntil(cts.Token)\n    .ForEachAsync(x =\u003e Console.WriteLine($\"Timer\"));\n\nsubscription.Dispose();\n```\n\nThe surface API remains the same as normal Rx, but the interfaces used internally are different and are not `IObservable\u003cT\u003e/IObserver\u003cT\u003e`.\n\n`IObservable\u003cT\u003e` being the dual of `IEnumerable\u003cT\u003e` is a beautiful definition, but it was not very practical in use.\n\n```csharp\npublic abstract class Observable\u003cT\u003e\n{\n    public IDisposable Subscribe(Observer\u003cT\u003e observer);\n}\n\npublic abstract class Observer\u003cT\u003e : IDisposable\n{\n    public void OnNext(T value);\n    public void OnErrorResume(Exception error);\n    public void OnCompleted(Result result); // Result is (Success | Failure)\n}\n```\n\nThe biggest difference is that in normal Rx, when an exception occurs in the pipeline, it flows to `OnError` and the subscription is unsubscribed, but in R3, it flows to `OnErrorResume` and the subscription is not unsubscribed.\n\nI consider the automatic unsubscription by OnError to be a bad design for event handling. It's very difficult and risky to resolve it within an operator like Retry, and it also led to poor performance (there are many questions and complex answers about stopping and resubscribing all over the world). Also, converting OnErrorResume to OnError(OnCompleted(Result.Failure)) is easy and does not degrade performance, but the reverse is impossible. Therefore, the design was changed to not stop by default and give users the choice to stop.\n\nSince the original Rx contract was `OnError | OnCompleted`, it was changed to `OnCompleted(Result result)` to consolidate into one method. Result is a readonly struct with two states: `Success() | Failure(Exception)`.\n\nThe reason for changing to an abstract class instead of an interface is that Rx has implicit complex contracts that interfaces do not guarantee. By making it an abstract class, we fully controlled the behavior of Subscribe, OnNext, and Dispose. This made it possible to manage the list of all subscriptions and prevent subscription leaks.\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/149abca5-6d84-44ea-8373-b0e8cd2dc46a)\n\nSubscription leaks are a common problem in applications with long lifecycles, such as GUIs or games. Tracking all subscriptions makes it easy to prevent leaks.\n\nInternally, when subscribing, an Observer is always linked to the target Observable and doubles as a Subscription. This ensures that Observers are reliably connected from top to bottom, making tracking certain and clear that they are released on OnCompleted/Dispose. In terms of performance, because the Observer itself always becomes a Subscription, there is no need for unnecessary IDisposable allocations.\n\nTimeProvider instead of IScheduler\n---\nIn traditional Rx, `IScheduler` was used as an abstraction for time-based processing, but in R3, we have discontinued its use and instead opted for the [TimeProvider](https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider?view=net-8.0) introduced in .NET 8. For example, the operators are defined as follows:\n\n```csharp\npublic static Observable\u003cUnit\u003e Interval(TimeSpan period, TimeProvider timeProvider);\npublic static Observable\u003cT\u003e Delay\u003cT\u003e(this Observable\u003cT\u003e source, TimeSpan dueTime, TimeProvider timeProvider)\npublic static Observable\u003cT\u003e Debounce\u003cT\u003e(this Observable\u003cT\u003e source, TimeSpan timeSpan, TimeProvider timeProvider) // same as Throttle in dotnet/reactive\n```\n\nOriginally, `IScheduler` had performance issues, and the internal implementation of dotnet/reactive was peppered with code that circumvented these issues using `PeriodicTimer` and `IStopwatch`, leading to unnecessary complexity. These can be better expressed with TimeProvider (`TimeProvider.CreateTimer()`, `TimeProvider.GetTimestamp()`).\n\nWhile TimeProvider is an abstraction for asynchronous operations, excluding the Fake for testing purposes, `IScheduler` included synchronous schedulers like `ImmediateScheduler` and `CurrentThreadScheduler`. However, these were also meaningless as applying them to time-based operators would cause blocking, and `CurrentThreadScheduler` had poor performance.\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/68a12664-a840-4725-a87c-8fdbb03b4a02)\n`Observable.Range(1, 10000).Subscribe()`\n\nIn R3, anything that requires synchronous execution (like Range) is treated as Immediate, and everything else is considered asynchronous and handled through TimeProvider.\n\nAs for the implementation of TimeProvider, the standard TimeProvider.System using the ThreadPool is the default. For unit testing, FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) is available. Additionally, many TimeProvider implementations are provided for different platforms, such as DispatcherTimeProvider for WPF and UpdateTimeProvider for Unity, enhancing ease of use tailored to each platform.\n\nFrame based operations\n---\nIn GUI applications, there's the message loop, and in game engines, there's the game loop. Platforms that operate based on loops are not uncommon. The idea of executing something after a few seconds or frames fits very well with Rx. Just as time has been abstracted through TimeProvider, we introduced a layer of abstraction for frames called FrameProvider, and added frame-based operators corresponding to all methods that accept TimeProvider.\n\n```csharp\npublic static Observable\u003cUnit\u003e IntervalFrame(int periodFrame, FrameProvider frameProvider);\npublic static Observable\u003cT\u003e DelayFrame\u003cT\u003e(this Observable\u003cT\u003e source, int frameCount, FrameProvider frameProvider)\npublic static Observable\u003cT\u003e DebounceFrame\u003cT\u003e(this Observable\u003cT\u003e source, int frameCount, FrameProvider frameProvider)\n```\n\nThe effectiveness of frame-based processing has been proven in Unity's Rx implementation, [neuecc/UniRx](https://github.com/neuecc/UniRx), which is one of the reasons why UniRx has gained strong support.\n\nThere are also several operators unique to frame-based processing.\n\n```csharp\n// push OnNext every frame.\nObservable.EveryUpdate().Subscribe(x =\u003e Console.WriteLine(x));\n\n// take value until next frame\neventSoure.TakeUntil(Observable.NextFrame()).Subscribe();\n\n// polling value changed\nObservable.EveryValueChanged(this, x =\u003e x.Width).Subscribe(x =\u003e WidthText.Text = x.ToString());\nObservable.EveryValueChanged(this, x =\u003e x.Height).Subscribe(x =\u003e HeightText.Text = x.ToString());\n```\n\n`EveryValueChanged` could be interesting, as it converts properties without Push-based notifications like `INotifyPropertyChanged`.\n\n![](https://cloud.githubusercontent.com/assets/46207/15827886/1573ff16-2c48-11e6-9876-4e4455d7eced.gif)`\n\nSubjects(ReactiveProperty)\n---\nIn R3, there are five types of Subjects: `Subject`, `BehaviorSubject`, `ReactiveProperty`, `ReplaySubject`, and `ReplayFrameSubject`.\n\n`Subject` is an event in Rx. Just as an event can register multiple Actions and distribute values using Invoke, a `Subject` can register multiple `Observer`s and distribute values using OnNext, OnErrorResume, and OnCompleted. There are variations of Subject, such as `BehaviorSubject` and `ReactiveProperty`, which holds a single value internally, `ReplaySubject`, which holds multiple values based on count or time, and `ReplayFrameSubject`, which holds multiple values based on frame time. The internally recorded values are distributed when Subscribe is called.\n\n `ReactiveProperty` corresponds to what would be a `BehaviorSubject`, but with the added functionality of eliminating duplicate values. In addition, since the value can be set with `.Value`, it can be utilized for binding on XAML platforms, etc.\n\nHere's an example of creating an observable model using `ReactiveProperty`:\n\n```csharp\n// Reactive Notification Model\npublic class Enemy\n{\n    public ReactiveProperty\u003clong\u003e CurrentHp { get; private set; }\n\n    public ReactiveProperty\u003cbool\u003e IsDead { get; private set; }\n\n    public Enemy(int initialHp)\n    {\n        // Declarative Property\n        CurrentHp = new ReactiveProperty\u003clong\u003e(initialHp);\n        IsDead = CurrentHp.Select(x =\u003e x \u003c= 0).ToReactiveProperty();\n    }\n}\n\n// ---\n\n// Click button, HP decrement\nMyButton.OnClickAsObservable().Subscribe(_ =\u003e enemy.CurrentHp.Value -= 99);\n\n// subscribe from notification model.\nenemy.CurrentHp.Subscribe(x =\u003e Console.WriteLine(\"HP:\" + x));\nenemy.IsDead.Where(isDead =\u003e isDead == true)\n    .Subscribe(_ =\u003e\n    {\n        // when dead, disable button\n        MyButton.SetDisable();\n    });\n```\n\nIn `ReactiveProperty`, the value is updated by `.Value` and if it is identical to the current value, no notification is issued. If you want to force notification of a value even if it is the same, call `.OnNext(value)`.\n\n`ReactiveProperty` has equivalents in other frameworks as well, such as [Android LiveData](https://developer.android.com/topic/libraries/architecture/livedata) and [Kotlin StateFlow](https://developer.android.com/kotlin/flow/stateflow-and-sharedflow), particularly effective for data binding in UI contexts. In .NET, there is a library called [runceel/ReactiveProperty](https://github.com/runceel/ReactiveProperty), which I originally created.\n\nUnlike dotnet/reactive's Subject, all Subjects in R3 (Subject, BehaviorSubject, ReactiveProperty, ReplaySubject, ReplayFrameSubject) are designed to call OnCompleted upon disposal. This is because R3 is designed with a focus on subscription management and unsubscription. By calling OnCompleted, it ensures that all subscriptions are unsubscribed from the Subject, the upstream source of events, by default. If you wish to avoid calling OnCompleted, you can do so by calling `Dispose(false)`.\n\n`ReactiveProperty` is mutable, but it can be converted to a read-only `ReadOnlyReactiveProperty`. Following the [guidance for the Android UI Layer](https://developer.android.com/topic/architecture/ui-layer), the Kotlin code below is\n\n```kotlin\nclass NewsViewModel(...) : ViewModel() {\n\n    private val _uiState = MutableStateFlow(NewsUiState())\n    val uiState: StateFlow\u003cNewsUiState\u003e = _uiState.asStateFlow()\n    ...\n}\n```\n\ncan be adapted to the following R3 code.\n\n```csharp\nclass NewsViewModel\n{\n    ReactiveProperty\u003cNewsUiState\u003e _uiState = new(new NewsUiState());\n    public ReadOnlyReactiveProperty\u003cNewsUiState\u003e UiState =\u003e _uiState;\n}\n```\n\nIn R3, we use a combination of a mutable private field and a readonly public property.\n\nBy inheriting `ReactiveProperty` and overriding `OnValueChanging` and `OnValueChanged`, you can customize behavior, such as adding validation.\n\n```csharp\n// Since the primary constructor sets values to fields before calling base, it is safe to call OnValueChanging in the base constructor.\npublic sealed class ClampedReactiveProperty\u003cT\u003e(T initialValue, T min, T max)\n    : ReactiveProperty\u003cT\u003e(initialValue) where T : IComparable\u003cT\u003e\n{\n    private static IComparer\u003cT\u003e Comparer { get; } = Comparer\u003cT\u003e.Default;\n\n    protected override void OnValueChanging(ref T value)\n    {\n        if (Comparer.Compare(value, min) \u003c 0)\n        {\n            value = min;\n        }\n        else if (Comparer.Compare(value, max) \u003e 0)\n        {\n            value = max;\n        }\n    }\n}\n\n// For regular constructors, please set `callOnValueChangeInBaseConstructor` to false and manually call it once to correct the value.\npublic sealed class ClampedReactiveProperty2\u003cT\u003e\n    : ReactiveProperty\u003cT\u003e where T : IComparable\u003cT\u003e\n{\n    private static IComparer\u003cT\u003e Comparer { get; } = Comparer\u003cT\u003e.Default;\n\n    readonly T min, max;\n\n    // callOnValueChangeInBaseConstructor to avoid OnValueChanging call before min, max set.\n    public ClampedReactiveProperty2(T initialValue, T min, T max)\n        : base(initialValue, EqualityComparer\u003cT\u003e.Default, callOnValueChangeInBaseConstructor: false)\n    {\n        this.min = min;\n        this.max = max;\n\n        // modify currentValue manually\n        OnValueChanging(ref GetValueRef());\n    }\n\n    protected override void OnValueChanging(ref T value)\n    {\n        if (Comparer.Compare(value, min) \u003c 0)\n        {\n            value = min;\n        }\n        else if (Comparer.Compare(value, max) \u003e 0)\n        {\n            value = max;\n        }\n    }\n}\n```\n\nAdditionally, `ReactiveProperty` supports serialization with `System.Text.JsonSerializer` in .NET 6 and above. For earlier versions, you need to implement `ReactivePropertyJsonConverterFactory` under the existing implementation and add it to the Converter.\n\nAs an internal implementation, `Subject` and `ReactiveProperty` has a lightweight implementation that consumes less memory. However, in exchange, its behavior differs slightly, especially in multi-threaded environments. For precautions related to multi-threading, please refer to the [Concurrency Policy](#concurrency-policy) section.\n\nDisposable\n---\nTo bundle multiple IDisposables (Subscriptions), it's good to use Disposable's methods. In R3, depending on the performance,\n\n```csharp\nDisposable.Combine(IDisposable d1, ..., IDisposable d8);\nDisposable.Combine(params IDisposable[]);\nDisposable.CreateBuilder();\nCompositeDisposable\nDisposableBag\n```\n\nfive types are available for use. In terms of performance advantages, the order is `Combine(d1,...,d8) (\u003e= CreateBuilder) \u003e Combine(IDisposable[]) \u003e= CreateBuilder \u003e DisposableBag \u003e CompositeDisposable`.\n\nWhen the number of subscriptions is statically determined, Combine offers the best performance. Internally, for less than 8 arguments, it uses fields, and for 9 or more arguments, it uses an array, making Combine especially efficient for 8 arguments or less.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    IDisposable disposable;\n\n    public MainWindow()\n    {\n        var d1 = Observable.IntervalFrame(1).Subscribe();\n        var d2 = Observable.IntervalFrame(1).Subscribe();\n        var d3 = Observable.IntervalFrame(1).Subscribe();\n\n        disposable = Disposable.Combine(d1, d2, d3);\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        disposable.Dispose();\n    }\n}\n```\n\nIf there are many subscriptions and it's cumbersome to hold each one in a variable, `CreateBuilder` can be used instead. At build time, it combines according to the number of items added to it. Since the Builder itself is a struct, there are no allocations.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    IDisposable disposable;\n\n    public MainWindow()\n    {\n        var d = Disposable.CreateBuilder();\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref d);\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref d);\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref d);\n\n        disposable = d.Build();\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        disposable.Dispose();\n    }\n}\n```\n\nFor dynamically added items, using `DisposableBag` is advisable. This is an add-only struct with only `Add/Clear/Dispose` methods. It can be used relatively quickly and with low allocation by holding it in a class field and passing it around by reference. However, it is not thread-safe.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    DisposableBag disposable; // DisposableBag is struct, no need new and don't copy\n\n    public MainWindow()\n    {\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref disposable);\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref disposable);\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref disposable);\n    }\n\n    void OnClick()\n    {\n        Observable.IntervalFrame(1).Subscribe().AddTo(ref disposable);\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        disposable.Dispose();\n    }\n}\n```\n\n`CompositeDisposable` is a class that also supports `Remove` and is thread-safe. It is the most feature-rich, but comparatively, it has the lowest performance.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    CompositeDisposable disposable = new CompositeDisposable();\n\n    public MainWindow()\n    {\n        Observable.IntervalFrame(1).Subscribe().AddTo(disposable);\n        Observable.IntervalFrame(1).Subscribe().AddTo(disposable);\n        Observable.IntervalFrame(1).Subscribe().AddTo(disposable);\n    }\n\n    void OnClick()\n    {\n        Observable.IntervalFrame(1).Subscribe().AddTo(disposable);\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        disposable.Dispose();\n    }\n}\n```\n\nAdditionally, there are other utilities for Disposables as follows.\n\n```csharp\nDisposable.Create(Action);\nDisposable.Dispose(...);\nSingleAssignmentDisposable\nSingleAssignmentDisposableCore // struct\nSerialDisposable\nSerialDisposableCore // struct\n```\n\nSubscription Management\n---\nManaging subscriptions is one of the most crucial aspects of Rx, and inadequate management can lead to memory leaks. There are two patterns for unsubscribing in Rx. One is by disposing of the IDisposable (Subscription) returned by Subscribe. The other is by receiving OnCompleted.\n\nIn R3, to enhance subscription cancellation on both fronts, it's now possible to bundle subscriptions using a variety of Disposable classes for Subscriptions, and for OnCompleted, the upstream side of events (such as Subject or Factory) has been made capable of emitting OnCompleted. Especially, Factories that receive a TimeProvider or FrameProvider can now take a CancellationToken.\n\n```csharp\npublic static Observable\u003cUnit\u003e Interval(TimeSpan period, TimeProvider timeProvider, CancellationToken cancellationToken)\npublic static Observable\u003cUnit\u003e EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken)\n```\n\nWhen cancelled, OnCompleted is sent, and all subscriptions are unsubscribed.\n\n### ObservableTracker\n\nR3 incorporates a system called ObservableTracker. When activated, it allows you to view all subscription statuses.\n\n```csharp\nObservableTracker.EnableTracking = true; // default is false\nObservableTracker.EnableStackTrace = true;\n\nusing var d = Observable.Interval(TimeSpan.FromSeconds(1))\n    .Where(x =\u003e true)\n    .Take(10000)\n    .Subscribe();\n\n// check subscription\nObservableTracker.ForEachActiveTask(x =\u003e\n{\n    Console.WriteLine(x);\n});\n```\n\n```csharp\nTrackingState { TrackingId = 1, FormattedType = Timer._Timer, AddTime = 2024/01/09 4:11:39, StackTrace =... }\nTrackingState { TrackingId = 2, FormattedType = Where`1._Where\u003cUnit\u003e, AddTime = 2024/01/09 4:11:39, StackTrace =... }\nTrackingState { TrackingId = 3, FormattedType = Take`1._Take\u003cUnit\u003e, AddTime = 2024/01/09 4:11:39, StackTrace =... }\n```\n\nBesides directly calling `ForEachActiveTask`, making it more accessible through a GUI can make it easier to check for subscription leaks. Currently, there is an integrated GUI for Unity, and there are plans to provide a screen using Blazor for other platforms.\n\nObservableSystem, UnhandledExceptionHandler\n---\nFor time-based operators that do not specify a TimeProvider or FrameProvider, the default Provider of ObservableSystem is used. This is settable, so if there is a platform-specific Provider (for example, DispatcherTimeProvider in WPF), you can swap it out to create a more user-friendly environment.\n\n```csharp\npublic static class ObservableSystem\n{\n    public static TimeProvider DefaultTimeProvider { get; set; } = TimeProvider.System;\n    public static FrameProvider DefaultFrameProvider { get; set; } = new NotSupportedFrameProvider();\n\n    static Action\u003cException\u003e unhandledException = DefaultUnhandledExceptionHandler;\n\n    // Prevent +=, use Set and Get method.\n    public static void RegisterUnhandledExceptionHandler(Action\u003cException\u003e unhandledExceptionHandler)\n    {\n        unhandledException = unhandledExceptionHandler;\n    }\n\n    public static Action\u003cException\u003e GetUnhandledExceptionHandler()\n    {\n        return unhandledException;\n    }\n\n    static void DefaultUnhandledExceptionHandler(Exception exception)\n    {\n        Console.WriteLine(\"R3 UnhandledException: \" + exception.ToString());\n    }\n}\n```\n\nIn CUI environments, by default, the FrameProvider will throw an exception. If you want to use FrameProvider in a CUI environment, you can set either `NewThreadSleepFrameProvider`, which sleeps in a new thread for a specified number of seconds, or `TimerFrameProvider`, which executes every specified number of seconds.\n\n### UnhandledExceptionHandler\n\nWhen an exception passes through OnErrorResume and is not ultimately handled by Subscribe, the UnhandledExceptionHandler of ObservableSystem is called. This can be set with `RegisterUnhandledExceptionHandler`. By default, it writes to `Console.WriteLine`, but it may need to be changed to use `ILogger` or something else as required.\n\nResult Handling\n---\nThe `Result` received by OnCompleted has a field `Exception?`, where it's null in case of success and contains the Exception in case of failure.\n\n```csharp\n// Typical processing code example\nvoid OnCompleted(Result result)\n{\n    if (result.IsFailure)\n    {\n        // do failure\n        _ = result.Exception;\n    }\n    else // result.IsSuccess\n    {\n        // do success\n    }\n}\n```\n\nTo generate a `Result`, in addition to using `Result.Success` and `Result.Failure(exception)`, Observer has OnCompleted() and OnCompleted(exception) as shortcuts for Success and Failure, respectively.\n\n```csharp\nobserver.OnCompleted(Result.Success);\nobserver.OnCompleted(Result.Failure(exception));\n\nobserver.OnCompleted(); // same as Result.Success\nobserver.OnCompleted(exception); // same as Result.Failure(exception)\n```\n\nUnit Testing\n---\nFor unit testing, you can use [FakeTimeProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.time.testing.faketimeprovider) of Microsoft.Extensions.TimeProvider.Testing.\n\nAdditionally, in R3, there is a collection called LiveList, which allows you to obtain subscription statuses as a list. Combining these two features can be very useful for unit testing.\n\n```csharp\nvar fakeTime = new FakeTimeProvider();\n\nvar list = Observable.Timer(TimeSpan.FromSeconds(5), fakeTime).ToLiveList();\n\nfakeTime.Advance(TimeSpan.FromSeconds(4));\nlist.AssertIsNotCompleted();\n\nfakeTime.Advance(TimeSpan.FromSeconds(1));\nlist.AssertIsCompleted();\nlist.AssertEqual([Unit.Default]);\n```\n\nFor FrameProvider, a `FakeFrameProvider` is provided as standard, and it can be used in the same way as `FakeTimeProvider`.\n\n```csharp\nvar cts = new CancellationTokenSource();\nvar frameProvider = new FakeFrameProvider();\n\nvar list = Observable.EveryUpdate(frameProvider, cts.Token)\n    .Select(_ =\u003e frameProvider.GetFrameCount())\n    .ToLiveList();\n\nlist.AssertEqual([]); // list.ShouldBe(expected);\n\nframeProvider.Advance();\nlist.AssertEqual([0]);\n\nframeProvider.Advance(3);\nlist.AssertEqual([0, 1, 2, 3]);\n\ncts.Cancel();\nlist.AssertIsCompleted(); // list.IsCompleted.ShouldBeTrue();\n\nframeProvider.Advance();\nlist.AssertEqual([0, 1, 2, 3]);\nlist.AssertIsCompleted();\n```\n\n`AssertEqual` is a test helper. You can create your own helper to use with the test library.\n\n```csharp\npublic static class LiveListExtensions\n{\n    // Shouldbe() is xUnit + Shouldly\n    public static void AssertEqual\u003cT\u003e(this LiveList\u003cT\u003e list, params T[] expected)\n    {\n        list.ShouldBe(expected);\n    }\n\n    public static void AssertEqual\u003cT\u003e(this LiveList\u003cT[]\u003e list, params T[][] expected)\n    {\n        list.Count.ShouldBe(expected.Length);\n\n        for (int i = 0; i \u003c expected.Length; i++)\n        {\n            list[i].ShouldBe(expected[i]);\n        }\n    }\n\n    public static void AssertEmpty\u003cT\u003e(this LiveList\u003cT\u003e list)\n    {\n        list.Count.ShouldBe(0);\n    }\n\n    public static void AssertIsCompleted\u003cT\u003e(this LiveList\u003cT\u003e list)\n    {\n        list.IsCompleted.ShouldBeTrue();\n    }\n\n    public static void AssertIsNotCompleted\u003cT\u003e(this LiveList\u003cT\u003e list)\n    {\n        list.IsCompleted.ShouldBeFalse();\n    }\n\n    public static void Advance(this FakeTimeProvider timeProvider, int seconds)\n    {\n        timeProvider.Advance(TimeSpan.FromSeconds(seconds));\n    }\n}\n```\n\nInteroperability with `IObservable\u003cT\u003e`\n---\n`Observable\u003cT\u003e` is not `IObservable\u003cT\u003e`. You can convert both by these methods.\n\n* `public static Observable\u003cT\u003e ToObservable\u003cT\u003e(this IObservable\u003cT\u003e source)`\n* `public static IObservable\u003cT\u003e AsSystemObservable\u003cT\u003e(this Observable\u003cT\u003e source)`\n\nInteroperability with `async/await`\n---\nR3 has special integration with `async/await`. First, all methods that return a single asynchronous operation have now become ***Async methods, returning `Task\u003cT\u003e`.\n\nMethods that convert to such `Task` (for example `FirstAsync`, `LastAsync`) transform OnErrorResume's Exception into a Faulted Task, similar to OnCompleted(Exception). Note that since the Catch operator does not capture OnErrorResume, if you want integrated error handling, please use `OnErrorResumeAsFailure()` to convert `OnErrorResume(Exception)` to `OnCompleted(Exception)`.\n\nFurthermore, you can specify special behaviors when asynchronous methods are provided to Where/Select/Subscribe.\n\n| Name | ReturnType |\n| --- | --- |\n| **SelectAwait**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003cTResult\u003e\u003e` selector, `AwaitOperation` awaitOperation = AwaitOperation.Sequential, `bool` configureAwait = true, `bool` cancelOnCompleted = true, `int` maxConcurrent = -1) | `Observable\u003cTResult\u003e` |\n| **WhereAwait**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003cBoolean\u003e\u003e` predicate, `AwaitOperation` awaitOperation = AwaitOperation.Sequential, `bool` configureAwait = true, `bool` cancelOnCompleted = true, `int` maxConcurrent = -1) | `Observable\u003cT\u003e` |\n| **SubscribeAwait**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` onNextAsync, `AwaitOperation` awaitOperation = AwaitOperation.Sequential, `bool` configureAwait = true, `bool` cancelOnCompleted = true, `int` maxConcurrent = -1) | `IDisposable` |\n| **SubscribeAwait**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` onNextAsync, `Action\u003cResult\u003e` onCompleted, `AwaitOperation` awaitOperation = AwaitOperation.Sequential, `bool` configureAwait = true, `bool` cancelOnCompleted = true, `int` maxConcurrent = -1) | `IDisposable` |\n| **SubscribeAwait**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` onNextAsync, `Action\u003cException\u003e` onErrorResume, `Action\u003cResult\u003e` onCompleted, `AwaitOperation` awaitOperation = AwaitOperation.Sequential, `bool` configureAwait = true, `bool` cancelOnCompleted = true, `int` maxConcurrent = -1) | `IDisposable` |\n\n```csharp\npublic enum AwaitOperation\n{\n    /// \u003csummary\u003eAll values are queued, and the next value waits for the completion of the asynchronous method.\u003c/summary\u003e\n    Sequential,\n    /// \u003csummary\u003eDrop new value when async operation is running.\u003c/summary\u003e\n    Drop,\n    /// \u003csummary\u003eIf the previous asynchronous method is running, it is cancelled and the next asynchronous method is executed.\u003c/summary\u003e\n    Switch,\n    /// \u003csummary\u003eAll values are sent immediately to the asynchronous method.\u003c/summary\u003e\n    Parallel,\n    /// \u003csummary\u003eAll values are sent immediately to the asynchronous method, but the results are queued and passed to the next operator in order.\u003c/summary\u003e\n    SequentialParallel,\n    /// \u003csummary\u003eSend the first value and the last value while the asynchronous method is running.\u003c/summary\u003e\n    ThrottleFirstLast\n}\n```\n\n```csharp\n// for example...\n// Drop enables prevention of execution by multiple clicks\nbutton.OnClickAsObservable()\n    .SelectAwait(async (_, ct) =\u003e\n    {\n        var req = await UnityWebRequest.Get(\"https://google.com/\").SendWebRequest().WithCancellation(ct);\n        return req.downloadHandler.text;\n    }, AwaitOperation.Drop)\n    .SubscribeToText(text);\n```\n\n`maxConcurrent` is only effective for `Parallel` and `SequentialParallel`, allowing control over the number of parallel operations. By default, it allows unlimited parallelization.\n\n`cancelOnCompleted` lets you choose whether to cancel the ongoing asynchronous method (by setting CancellationToken to Cancel) when the `OnCompleted` event is received. The default is true, meaning it will be cancelled. If set to false, it waits for the completion of the asynchronous method before calling the subsequent `OnCompleted` (potentially after issuing OnNext, depending on the case).\n\nAdditionally, the following time-related filtering/aggregating methods can also accept asynchronous methods.\n\n| Name | ReturnType |\n| --- | --- |\n| **Debounce**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` throttleDurationSelector, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` |\n| **ThrottleFirst**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` sampler, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` |\n| **ThrottleLast**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` sampler, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` |\n| **ThrottleFirstLast**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` sampler, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` |\n| **SkipUntil**(this `Observable\u003cT\u003e` source, `CancellationToken` cancellationToken) | `Observable\u003cT\u003e` | \n| **SkipUntil**(this `Observable\u003cT\u003e` source, `Task` task) | `Observable\u003cT\u003e` | \n| **SkipUntil**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` asyncFunc, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` | \n| **TakeUntil**(this `Observable\u003cT\u003e` source, `CancellationToken` cancellationToken) | `Observable\u003cT\u003e` | \n| **TakeUntil**(this `Observable\u003cT\u003e` source, `Task` task) | `Observable\u003cT\u003e` | \n| **TakeUntil**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` asyncFunc, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` | \n| **Chunk**(this `Observable\u003cT\u003e` source, `Func\u003cT, CancellationToken, ValueTask\u003e` asyncWindow, `Boolean` configureAwait = true) | `Observable\u003cT[]\u003e` | \n\nFor example, by using the asynchronous function version of Chunk, you can naturally and easily write complex processes such as generating chunks at random times instead of fixed times.\n\n```csharp\nObservable.Interval(TimeSpan.FromSeconds(1))\n    .Index()\n    .Chunk(async (_, ct) =\u003e\n    {\n        await Task.Delay(TimeSpan.FromSeconds(Random.Shared.Next(0, 5)), ct);\n    })\n    .Subscribe(xs =\u003e\n    {\n        Console.WriteLine(string.Join(\", \", xs));\n    });\n```\nThese asynchronous methods are immediately canceled when `OnCompleted` is issued, and the subsequent `OnCompleted` is executed.\n\nBy utilizing async/await for Retry-related operations, you can achieve better handling. For instance, whereas the previous version of Rx could only retry the entire pipeline, with R3, which accepts async/await, it is possible to retry on a per asynchronous method execution basis.\n\n```csharp\nbutton.OnClickAsObservable()\n    .SelectAwait(async (_, ct) =\u003e\n    {\n        var retry = 0;\n    AGAIN:\n        try\n        {\n            var req = await UnityWebRequest.Get(\"https://google.com/\").SendWebRequest().WithCancellation(ct);\n            return req.downloadHandler.text;\n        }\n        catch\n        {\n            if (retry++ \u003c 3) goto AGAIN;\n            throw;\n        }\n    }, AwaitOperation.Drop)\n```\n\nRepeat can also be implemented in combination with async/await. In this case, handling complex conditions for Repeat might be easier than completing it with Rx alone.\n\n```csharp\nwhile (!ct.IsCancellationRequested)\n{\n    await button.OnClickAsObservable()\n        .Take(1)\n        .ForEachAsync(_ =\u003e\n        {\n            // do something\n        });\n}\n```\n\nConcurrency Policy\n---\nThe composition of operators is thread-safe, and it is expected that the values flowing through OnNext are on a single thread. In other words, if OnNext is issued on multiple threads, the operators may behave unexpectedly. This is the same as with dotnet/reactive.\n\nFor example, while Subject itself is thread-safe, the operators are not thread-safe.\n\n```csharp\n// dotnet/reactive\nvar subject = new System.Reactive.Subjects.Subject\u003cint\u003e();\n\n// single execution shows 100 but actually 9* multiple times(broken)\nsubject.Take(100).Count().Subscribe(x =\u003e Console.WriteLine(x));\n\nParallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = 10 }, x =\u003e subject.OnNext(x));\n```\n\nThis means that the issuance of OnNext must always be done on a single thread. For converting external inputs into Observables, such as with `FromEvent`, and when the source of input issues in a multi-threaded manner, it is necessary to synchronize using `Synchronize` to construct the correct operator chain.\n\n```csharp\nsubject.Synchronize(gate).Take(100).Count().Subscribe();\n```\n\nUnlike dotnet/reactive, R3.Subject.OnNext is not ThreadSafe. If you are calling OnNext from multiple threads, please use a lock.\n\nIn R3, ReplaySubject and BehaviorSubject do not require Synchronize and are thread-safe, including OnNext.\n\nReactiveProperty is not thread-safe and OnNext, set Value and Subscribe cannot be called simultaneously. If you need to use it in such a situation, use `SynchronizedReactiveProperty` instead.\n\n```csharp\nclass MyClass\n{\n    public SynchronizedReactiveProperty\u003cint\u003e Prop { get; } = new();\n}\n```\n\nSampling Timing\n---\nThe `Sample(TimeSpan)` in dotnet/reactive starts a timer in the background when subscribed to, and uses that interval for filtering. Additionally, the timer continues to run in the background indefinitely.\n\n`ThrottleFirst/Last/FirstLast(TimeSpan)` in R3 behaves differently; the timer is stopped upon subscription and only starts when a value arrives. If the timer is stopped at that time, it starts, and then stops the timer after the specified duration.\n\nAlso, overloads that accept an asynchronous function `Func\u003cT, CancellationToken, ValueTask\u003e`, such as `ThrottleFirst/Last/FirstLast`, `Chunk`, `SkipUntil`, `TakeUntil`), behave in such a way that if the asynchronous function is not running when a value arrives, the execution of the asynchronous function begins.\n\nThis change is expected to result in consistent behavior across all operators.\n\nObservableCollections\n---\nAs a special collection for monitoring changes in collections and handling them in R3, the [ObservableCollections](https://github.com/Cysharp/ObservableCollections)'s `ObservableCollections.R3` package is available.\n\nIt has `ObservableList\u003cT\u003e`, `ObservableDictionary\u003cTKey, TValue\u003e`, `ObservableHashSet\u003cT\u003e`, `ObservableQueue\u003cT\u003e`, `ObservableStack\u003cT\u003e`, `ObservableRingBuffer\u003cT\u003e`, `ObservableFixedSizeRingBuffer\u003cT\u003e` and these observe methods.\n\n```csharp\nObservable\u003cCollectionAddEvent\u003cT\u003e\u003e IObservableCollection\u003cT\u003e.ObserveAdd()\nObservable\u003cCollectionRemoveEvent\u003cT\u003e\u003e IObservableCollection\u003cT\u003e.ObserveRemove()\nObservable\u003cCollectionReplaceEvent\u003cT\u003e\u003e IObservableCollection\u003cT\u003e.ObserveReplace()\nObservable\u003cCollectionMoveEvent\u003cT\u003e\u003e IObservableCollection\u003cT\u003e.ObserveMove()\nObservable\u003cCollectionResetEvent\u003cT\u003e\u003e IObservableCollection\u003cT\u003e.ObserveReset()\n```\n\nXAML Platforms(`BindableReactiveProperty\u003cT\u003e`)\n---\nFor XAML based application platforms, R3 provides `BindableReactiveProperty\u003cT\u003e` that can bind observable property to view like [Android LiveData](https://developer.android.com/topic/libraries/architecture/livedata) and [Kotlin StateFlow](https://developer.android.com/kotlin/flow/.stateflow-and-sharedflow). It implements [INotifyPropertyChanged](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged) and [INotifyDataErrorInfo](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifydataerrorinfo).\n\nSimple usage, expose `BindableReactiveProperty\u003cT\u003e` via `new` or `ToBindableReactiveProperty`.\n\nHere is the simple In and Out BindableReactiveProperty ViewModel, Xaml and code-behind. In xaml, `.Value` to bind property.\n\n```csharp\npublic class BasicUsagesViewModel : IDisposable\n{\n    public BindableReactiveProperty\u003cstring\u003e Input { get; }\n    public BindableReactiveProperty\u003cstring\u003e Output { get; }\n\n    public BasicUsagesViewModel()\n    {\n        Input = new BindableReactiveProperty\u003cstring\u003e(\"\");\n        Output = Input.Select(x =\u003e x.ToUpper()).ToBindableReactiveProperty(\"\");\n    }\n\n    public void Dispose()\n    {\n        Disposable.Dispose(Input, Output);\n    }\n}\n```\n\n```xml\n\u003cWindow x:Class=\"WpfApp1.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:WpfApp1\"\n        mc:Ignorable=\"d\"\n        Title=\"MainWindow\" Height=\"450\" Width=\"800\"\u003e\n    \u003cWindow.DataContext\u003e\n        \u003clocal:BasicUsagesViewModel /\u003e\n    \u003c/Window.DataContext\u003e\n    \u003cStackPanel\u003e\n        \u003cTextBlock Text=\"Basic usages\" FontSize=\"24\" /\u003e\n\n        \u003cLabel Content=\"Input\" /\u003e\n        \u003cTextBox Text=\"{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}\" /\u003e\n\n        \u003cLabel Content=\"Output\" /\u003e\n        \u003cTextBlock Text=\"{Binding Output.Value}\" /\u003e\n    \u003c/StackPanel\u003e\n\u003c/Window\u003e\n```\n\n```csharp\nnamespace WpfApp1;\n\npublic partial class MainWindow : Window\n{\n    public MainWindow()\n    {\n        InitializeComponent();\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        (this.DataContext as IDisposable)?.Dispose();\n    }\n}\n```\n\n![image](https://github.com/Cysharp/R3/assets/46207/01c3738f-e941-412e-b517-8e7867d6f709)\n\nBindableReactiveProperty also supports validation via DataAnnotation or custom logic. If you want to use DataAnnotation attribute, require to call `EnableValidation\u003cT\u003e()` in field initializer or `EnableValidation(Expression selfSelector)` in constructor.\n\n```csharp\npublic class ValidationViewModel : IDisposable\n{\n    // Pattern 1. use EnableValidation\u003cT\u003e to enable DataAnnotation validation in field initializer\n    [Range(0.0, 300.0)]\n    public BindableReactiveProperty\u003cdouble\u003e Height { get; } = new BindableReactiveProperty\u003cdouble\u003e().EnableValidation\u003cValidationViewModel\u003e();\n\n    [Range(0.0, 300.0)]\n    public BindableReactiveProperty\u003cdouble\u003e Weight { get; }\n\n    IDisposable customValidation1Subscription;\n    public BindableReactiveProperty\u003cdouble\u003e CustomValidation1 { get; set; }\n\n    public BindableReactiveProperty\u003cdouble\u003e CustomValidation2 { get; set; }\n\n    public ValidationViewModel()\n    {\n        // Pattern 2. use EnableValidation(Expression) to enable DataAnnotation validation\n        Weight = new BindableReactiveProperty\u003cdouble\u003e().EnableValidation(() =\u003e Weight);\n\n        // Pattern 3. EnableValidation() and call OnErrorResume to set custom error message\n        CustomValidation1 = new BindableReactiveProperty\u003cdouble\u003e().EnableValidation();\n        customValidation1Subscription = CustomValidation1.Subscribe(x =\u003e\n        {\n            if (0.0 \u003c= x \u0026\u0026 x \u003c= 300.0) return;\n\n            CustomValidation1.OnErrorResume(new Exception(\"value is not in range.\"));\n        });\n\n        // Pattern 4. simplified version of Pattern3, EnableValidation(Func\u003cT, Exception?\u003e)\n        CustomValidation2 = new BindableReactiveProperty\u003cdouble\u003e().EnableValidation(x =\u003e\n        {\n            if (0.0 \u003c= x \u0026\u0026 x \u003c= 300.0) return null; // null is no validate result\n            return new Exception(\"value is not in range.\");\n        });\n    }\n\n    public void Dispose()\n    {\n        Disposable.Dispose(Height, Weight, CustomValidation1, customValidation1Subscription, CustomValidation2);\n    }\n}\n```\n\n```xml\n\u003cWindow x:Class=\"WpfApp1.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:WpfApp1\"\n        mc:Ignorable=\"d\"\n        Title=\"MainWindow\" Height=\"450\" Width=\"800\"\u003e\n    \u003cWindow.DataContext\u003e\n        \u003clocal:ValidationViewModel /\u003e\n    \u003c/Window.DataContext\u003e\n\n    \u003cStackPanel Margin=\"10\"\u003e\n        \u003cLabel Content=\"Validation\" /\u003e\n        \u003cTextBox Text=\"{Binding Height.Value, UpdateSourceTrigger=PropertyChanged}\"  /\u003e\n        \u003cTextBox  Text=\"{Binding Weight.Value, UpdateSourceTrigger=PropertyChanged}\" /\u003e\n        \u003cTextBox  Text=\"{Binding CustomValidation1.Value, UpdateSourceTrigger=PropertyChanged}\" /\u003e\n        \u003cTextBox  Text=\"{Binding CustomValidation2.Value, UpdateSourceTrigger=PropertyChanged}\" /\u003e\n    \u003c/StackPanel\u003e\n\u003c/Window\u003e\n```\n\n![image](https://github.com/Cysharp/R3/assets/46207/f80149e6-1573-46b5-9a77-b78776dd3527)\n\nValidation using `EnableValidation` does not trigger for initial values by default. This means that even if you don't allow empty strings, the validation error won't appear for an initially empty value. If you want to perform validation on initial values, you can call `ForceValidate()` after `EnableValidation`.\n\n```csharp\nWeight = new BindableReactiveProperty\u003cdouble\u003e()\n    .EnableValidation(() =\u003e Weight)\n    .ForceValidate();\n```\n\nThere is also `IReadOnlyBindableReactiveProperty\u003cT\u003e`, which is preferable when ReadOnly is required in binding, can create from `IObservable\u003cT\u003e.ToReadOnlyBindableReactiveProperty\u003cT\u003e`.\n\n### ReactiveCommand\n\n`ReactiveCommand\u003cT\u003e` and `ReactiveCommand` are observable [ICommand](https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.icommand) implementation. It can create from `Observable\u003cbool\u003e canExecuteSource`.\n\n```csharp\npublic class CommandViewModel : IDisposable\n{\n    public BindableReactiveProperty\u003cbool\u003e OnCheck { get; } // bind to CheckBox\n    public ReactiveCommand ShowMessageBox { get; }   // bind to Button, non generics ReactiveCommand is ReactiveCommand\u003cUnit\u003e\n\n    public CommandViewModel()\n    {\n        OnCheck = new BindableReactiveProperty\u003cbool\u003e();\n        ShowMessageBox = OnCheck.ToReactiveCommand(_ =\u003e\n        {\n            MessageBox.Show(\"clicked\");\n        });\n    }\n\n    public void Dispose()\n    {\n        Disposable.Dispose(OnCheck, ShowMessageBox);\n    }\n}\n```\n\n```xml\n\u003cWindow x:Class=\"WpfApp1.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:WpfApp1\"\n        mc:Ignorable=\"d\"\n        Title=\"MainWindow\" Height=\"450\" Width=\"800\"\u003e\n    \u003cWindow.DataContext\u003e\n        \u003clocal:CommandViewModel /\u003e\n    \u003c/Window.DataContext\u003e\n    \u003cStackPanel Margin=\"10\"\u003e\n        \u003cLabel Content=\"Command\" /\u003e\n        \u003cCheckBox IsChecked=\"{Binding OnCheck.Value}\" /\u003e\n        \u003cButton Content=\"Btn\" Command=\"{Binding ShowMessageBox}\" /\u003e\n    \u003c/StackPanel\u003e\n\u003c/Window\u003e\n```\n\n![rpcommand](https://github.com/Cysharp/R3/assets/46207/c456829e-1493-446d-831b-425f05be5d05)\n\n### INotifyPropertyChanged to Observable\n\nTo convert properties of `INotifyPropertyChanged` and `INotifyPropertyChanging` into Observables, you can use `ObservePropertyChanged` and `ObservePropertyChanging`.\n\n```csharp\nvar person = new Person { Name = \"foo\" };\n\nperson.ObservePropertyChanged(x =\u003e x.Name)\n      .Subscribe(x =\u003e Console.WriteLine($\"Changed:{x}\"));\n\np.Name = \"bar\";\np.Name = \"baz\";\n```\n\n`Func\u003cT, TProperty\u003e propertySelector` only supports simple property name lambda. This is because, in R3, `CallerArgumentExpression` is used to extract, for example from `x =\u003e x.Name` to \"Name\".\n\n### FromEvent\n\nTo convert existing events into Observables, use FromEvent. Because it requires the conversion of delegates and has a unique way of calling, please refer to the following sample.\n\n```csharp\nObservable.FromEvent\u003cRoutedEventHandler, RoutedEventArgs\u003e(\n    h =\u003e (sender, e) =\u003e h(e),\n    e =\u003e button.Click += e,\n    e =\u003e button.Click -= e);\n```\n\nPlatform Supports\n---\nEven without adding specific platform support, it is possible to use only the core library. However, Rx becomes more user-friendly by replacing the standard `TimeProvider` and `FrameProvider` with those optimized for each platform. For example, while the standard `TimeProvider` is thread-based, using a UI thread-based `TimeProvider` for each platform can eliminate the need for dispatch through `ObserveOn`, enhancing usability. Additionally, since message loops differ across platforms, the use of individual `FrameProvider` is essential.\n\nAlthough standard support is provided for the following platforms, by implementing `TimeProvider` and `FrameProvider`, it is possible to support any environment, including in-house game engine or other frameworks.\n\n* [WPF](#wpf)\n* [Avalonia](#avalonia)\n* [Uno](#uno)\n* [MAUI](#mau)\n* [WinForms](#winforms)\n* [WinUI3](#winui3)\n* [Unity](#unity)\n* [Godot](#godot)\n* [Stride](#stride)\n* [MonoGame](#monogame)\n* [LogicLooper](#logiclooper)\n* [Blazor](#blazor)\n\n### WPF\n\n\u003e PM\u003e Install-Package [R3Extensions.WPF](https://www.nuget.org/packages/R3Extensions.WPF)\n\nR3Extensions.WPF package has two providers.\n\n* WpfDispatcherTimeProvider\n* WpfRenderingFrameProvider\n\nCalling `WpfProviderInitializer.SetDefaultObservableSystem()` at startup will replace `ObservableSystem.DefaultTimeProvider` and `ObservableSystem.DefaultFrameProvider` with the aforementioned providers.\n\n```csharp\npublic partial class App : Application\n{\n    protected override void OnStartup(StartupEventArgs e)\n    {\n        // You need to set UnhandledExceptionHandler\n        WpfProviderInitializer.SetDefaultObservableSystem(ex =\u003e Trace.WriteLine($\"R3 UnhandledException:{ex}\"));\n    }\n}\n```\n\nAs a result, time based operations are replaced with `DispatcherTimer`, allowing you to reflect time based operations on the UI without having to use `ObserveOn`.\n\n`WpfRenderingFrameProvider` is a frame-based loop system synchronized with the `CompositionTarget.Rendering` event. This allows for writing code that, for example, reads and reflects changes in values that do not implement `INotifyPropertyChanged`.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    IDisposable disposable;\n\n    public MainWindow()\n    {\n        InitializeComponent();\n\n        var d1 = Observable.EveryValueChanged(this, x =\u003e x.Width).Subscribe(x =\u003e WidthText.Text = x.ToString());\n        var d2 = Observable.EveryValueChanged(this, x =\u003e x.Height).Subscribe(x =\u003e HeightText.Text = x.ToString());\n\n        disposable = Disposable.Combine(d1, d2);\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        disposable.Dispose();\n    }\n}\n```\n\n![](https://cloud.githubusercontent.com/assets/46207/15827886/1573ff16-2c48-11e6-9876-4e4455d7eced.gif)\n\nIn addition to the above, the following `ObserveOn`/`SubscribeOn` methods have been added.\n\n* ObserveOnDispatcher\n* ObserveOnCurrentDispatcher\n* SubscribeOnDispatcher\n* SubscribeOnCurrentDispatcher\n\nViewModel binding support, see [`BindableReactiveProperty\u003cT\u003e`](#xaml-platformsbindablereactivepropertyt) section.\n\n### Avalonia\n\n\u003e PM\u003e Install-Package [R3Extensions.Avalonia](https://www.nuget.org/packages/R3Extensions.Avalonia)\n\nR3Extensions.Avalonia package has these providers.\n\n* AvaloniaDispatcherTimeProvider\n* AvaloniaDispatcherFrameProvider\n* AvaloniaRenderingFrameProvider\n\nCalling `AvaloniaProviderInitializer.SetDefaultObservableSystem()` at startup will replace `ObservableSystem.DefaultTimeProvider` and `ObservableSystem.DefaultFrameProvider` with `AvaloniaDispatcherTimeProvider` and `AvaloniaDispatcherFrameProvider`.\n\nAdditionally, calling `UseR3()` in `AppBuilder` sets the default providers, making it a recommended approach.\n\n```csharp\npublic static AppBuilder BuildAvaloniaApp()\n    =\u003e AppBuilder.Configure\u003cApp\u003e()\n        .UsePlatformDetect()\n        .WithInterFont()\n        .LogToTrace()\n        .UseR3(); // add this line\n```\n\nAs a result, time based operations are replaced with `DispatcherTimer`, allowing you to reflect time based operations on the UI without having to use `ObserveOn`.\n\nIn the case of methods without arguments, integrate the following method into `ObservableSystem.RegisterUnhandledExceptionHandler`. Please customize this as necessary.\n\n```csharp\nex =\u003e Logger.Sink?.Log(LogEventLevel.Error, \"R3\", null, \"R3 Unhandled Exception {0}\", ex);\n```\n\n`AvaloniaDispatcherFrameProvider` calculates a frame by polling with `DispatcherTimer`. By default, it updates at 60fps.\n\nUsing `AvaloniaRenderingFrameProvider` is more performant however it needs `TopLevel`.\n\n```csharp\npublic partial class MainWindow : Window\n{\n    AvaloniaRenderingFrameProvider frameProvider;\n\n    public MainWindow()\n    {\n        InitializeComponent();\n\n        // initialize RenderingFrameProvider\n        var topLevel = TopLevel.GetTopLevel(this);\n        this.frameProvider = new AvaloniaRenderingFrameProvider(topLevel!);\n    }\n\n    protected override void OnLoaded(RoutedEventArgs e)\n    {\n        // pass frameProvider\n        Observable.EveryValueChanged(this, x =\u003e x.Width, frameProvider)\n            .Subscribe(x =\u003e textBlock.Text = x.ToString());\n    }\n\n    protected override void OnClosed(EventArgs e)\n    {\n        frameProvider.Dispose();\n    }\n}\n```\n\nIn addition to the above, the following `ObserveOn`/`SubscribeOn` methods have been added.\n\n* ObserveOnDispatcher\n* ObserveOnUIThreadDispatcher\n* SubscribeOnDispatcher\n* SubscribeOnUIThreadDispatcher\n\n### Uno\n\n\u003e PM\u003e Install-Package [R3Extensions.Uno](https://www.nuget.org/packages/R3Extensions.Uno)\n\nR3Extensions.Uno package has two providers.\n\n* UnoDispatcherTimeProvider\n* UnoRenderingFrameProvider\n\nCalling `UnoProviderInitializer.SetDefaultObservableSystem()` at startup will replace `ObservableSystem.DefaultTimeProvider` and `ObservableSystem.DefaultFrameProvider` with `UnoDispatcherTimeProvider` and `UnoRenderingFrameProvider`.\n\nAdditionally, calling `UseR3()` in `ApplicationBuilder` sets the default providers, making it a recommended approach.\n\n```csharp\npublic partial class App : Application\n{\n    protected async override void OnLaunched(LaunchActivatedEventArgs args)\n    {\n        var builder = this.CreateBuilder(args)\n            .UseR3() // add this line\n            ...\n    }\n}\n```\n\nAs a result, time based operations are replaced with `DispatcherTimer`, allowing you to reflect time based operations on the UI without having to use `ObserveOn`.\n\nIn the case of methods without arguments, integrate the following method into `ObservableSystem.RegisterUnhandledExceptionHandler`. Please customize this as necessary.\n\n```csharp\nex =\u003e builder.Log().LogError(\"R3 Unhandled Exception {0}\", ex));\n```\n\nIn addition to the above, the following `ObserveOn`/`SubscribeOn` methods have been added.\n\n* ObserveOnDispatcher\n* ObserveOnCurrentWindowDispatcher\n* SubscribeOnDispatcher\n* SubscribeOnCurrentWindowDispatcher\n\n### MAUI\n\n\u003e PM\u003e Install-Package [R3Extensions.Maui](https://www.nuget.org/packages/R3Extensions.Maui)\n\nR3Extensions.Maui package has these providers.\n\n* MauiDispatcherTimeProvider\n* MauiTickerFrameProvider\n\nAnd ViewModel binding is supported, see [`BindableReactiveProperty\u003cT\u003e`](#xaml-platformsbindablereactivepropertyt) section.\n\nCalling `UseR3()` in `MauiAppBuilder` sets the default providers.\n\n```csharp\npublic static MauiApp CreateMauiApp()\n{\n    var builder = MauiApp.CreateBuilder();\n    builder\n        .UseMauiApp\u003cApp\u003e()\n        .ConfigureFonts(fonts =\u003e\n        {\n            fonts.AddFont(\"OpenSans-Regular.ttf\", \"OpenSansRegular\");\n            fonts.AddFont(\"OpenSans-Semibold.ttf\", \"OpenSansSemibold\");\n        })\n        .UseR3(); // add this line\n\n    return builder.Build();\n}\n```\n\n`UseR3()` configures the following.\n\n- Time based operations are replaced with `IDispatcher`, allowing you to reflect time based operations on the UI without having to use `ObserveOn`.\n- Frame based operations are replaced with `Ticker`.\n- `ObservableSystem.RegisterUnhandledExceptionHandler` is set to `R3MauiDefaultExceptionHandler`:\n    - ```csharp\n      public class R3MauiDefaultExceptionHandler(IServiceProvider serviceProvider) : IR3MauiExceptionHandler\n      {\n          public void HandleException(Exception ex)\n          {\n              System.Diagnostics.Trace.TraceError(\"R3 Unhandled Exception {0}\", ex);\n\n              var logger = serviceProvider.GetService\u003cILogger\u003cR3MauiDefaultExceptionHandler\u003e\u003e();\n              logger?.LogError(ex, \"R3 Unhandled Exception\");\n          }\n      }\n      ```\nIf you want to customize the ExceptionHandler, there are two ways.\n\nOne is to pass a callback to `UseR3e\n\n```csharp\nbuilder.UseR3(ex =\u003e Console.WriteLine($\"R3 UnhandledException:{ex}\"));\n```\n\nThe second is to create an implementation of the `IR3MAuiExceptionHandler` interface and DI it.\nSince MAUI is a DI-based framework, this method will make it easier to access the various functions in the DI container.\n\n```csharp\nbuilder.Services.AddSingleton\u003cIR3MauiExceptionHandler, YourCustomExceptionHandler\u003e();\n```\n\n### WinForms\n\n\u003e PM\u003e Install-Package [R3Extensions.WinForms](https://www.nuget.org/packages/R3Extensions.WinForms)\n\nR3Extensions.WinForms package has these providers.\n\n* WinFormsFrameProvider\n* WinFormsTimeProvider\n\nCalling `WinFormsProviderInitializer.SetDefaultObservableSystem()` at startup(Program.Main) will replace `ObservableSystem.DefaultTimeProvider` and `ObservableSystem.DefaultFrameProvider` with `WinFormsFrameProvider` and `WinFormsTimeProvider`.\n\n\n```csharp\nusing R3.WinForms;\n\ninternal static class Program\n{\n    [STAThread]\n    static void Main()\n    {\n        ApplicationConfiguration.Initialize();\n\n        var form = new Form1();\n\n        // add this line\n        WinFormsProviderInitializer.SetDefaultObservableSystem(ex =\u003e Trace.WriteLine($\"R3 UnhandledException:{ex}\"), form);\n\n        Application.Run(form);\n    }\n}\n```\n\n`SetDefaultObservableSystem` takes ISynchronizeInvoke (such as Form or Control). This makes the Timer operate on the thread to which it belongs.\n\nFrameProvider is executed as one frame using the hook of MessageFilter.\n\n### WinUI3\n\n\u003e PM\u003e Install-Package [R3Extensions.WinUI3](https://www.nuget.org/packages/R3Extensions.WinUI3)\n\nR3Extensions.WinUI3 package has these providers.\n\n* WinUI3DispatcherTimeProvider\n* WinUI3RenderingFrameProvider\n\nCalling `WinUI3ProviderInitializer.SetDefaultObservableSystem()` at startup will replace `ObservableSystem.DefaultTimeProvider` and `ObservableSystem.DefaultFrameProvider` with the aforementioned providers.\n\n```csharp\npublic partial class App : Application\n{\n    public App()\n    {\n        this.InitializeComponent();\n\n        // Add this line.\n        // You need to set UnhandledExceptionHandler\n        WinUI3ProviderInitializer.SetDefaultObservableSystem(ex =\u003e Trace.WriteLine(ex.ToString()));\n    }\n\n    // OnLaunched...\n}\n```\n\n### Unity\n\nThe minimum Unity support for R3 is **Unity 2021.3**.\n\nThere are two installation steps required to use it in Unity.\n\n1. Install `R3` from NuGet using [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)\n\n* Open Window from NuGet -\u003e Manage NuGet Packages, Search \"R3\" and Press Install.\n![](https://github.com/Cysharp/ZLogger/assets/46207/dbad9bf7-28e3-4856-b0a8-0ff8a2a01d67)\n\n* If you encounter version conflict errors, please disable version validation in Player Settings(Edit -\u003e Project Settings -\u003e Player -\u003e Scroll down and expand \"Other Settings\" than uncheck \"Assembly Version Validation\" under the \"Configuration\" section).\n\n2. Install the `R3.Unity` package by referencing the git URL\n\n```\nhttps://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3.Unity\n```\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/7325d266-05b4-47c9-b06a-a67a40368dd2)\n![image](https://github.com/Cysharp/ZLogger/assets/46207/29bf5636-4d6a-4e75-a3d8-3f8408bd8c51)\n\nR3 uses the *.*.* release tag, so you can specify a version like #1.0.0. For example: `https://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3.Unity#1.0.0`\n\nUnity's TimeProvider and FrameProvider is PlayerLoop based. Additionally, there are variations of TimeProvider that correspond to the TimeScale.\n\n```\nUnityTimeProvider.Initialization\nUnityTimeProvider.EarlyUpdate\nUnityTimeProvider.FixedUpdate\nUnityTimeProvider.PreUpdate\nUnityTimeProvider.Update\nUnityTimeProvider.PreLateUpdate\nUnityTimeProvider.PostLateUpdate\nUnityTimeProvider.TimeUpdate\n\nUnityTimeProvider.InitializationIgnoreTimeScale\nUnityTimeProvider.EarlyUpdateIgnoreTimeScale\nUnityTimeProvider.FixedUpdateIgnoreTimeScale\nUnityTimeProvider.PreUpdateIgnoreTimeScale\nUnityTimeProvider.UpdateIgnoreTimeScale\nUnityTimeProvider.PreLateUpdateIgnoreTimeScale\nUnityTimeProvider.PostLateUpdateIgnoreTimeScale\nUnityTimeProvider.TimeUpdateIgnoreTimeScale\n\nUnityTimeProvider.InitializationRealtime\nUnityTimeProvider.EarlyUpdateRealtime\nUnityTimeProvider.FixedUpdateRealtime\nUnityTimeProvider.PreUpdateRealtime\nUnityTimeProvider.UpdateRealtime\nUnityTimeProvider.PreLateUpdateRealtime\nUnityTimeProvider.PostLateUpdateRealtime\nUnityTimeProvider.TimeUpdateRealtime\n```\n\n```\nUnityFrameProvider.Initialization\nUnityFrameProvider.EarlyUpdate\nUnityFrameProvider.FixedUpdate\nUnityFrameProvider.PreUpdate\nUnityFrameProvider.Update\nUnityFrameProvider.PreLateUpdate\nUnityFrameProvider.PostLateUpdate\nUnityFrameProvider.TimeUpdate\n```\n\nYou can write it like this using these:\n\n```csharp\n// ignore-timescale based interval\nObservable.Interval(TimeSpan.FromSeconds(5), UnityTimeProvider.UpdateIgnoreTimeScale);\n\n// fixed-update loop\nObservable.EveryUpdate(UnityFrameProvider.FixedUpdate);\n\n// observe PostLateUpdate\nObservable.Return(42).ObserveOn(UnityFrameProvider.PostLateUpdate);\n```\n\nIn the case of Unity, `UnityTimeProvider.Update` and `UnityFrameProvider.Update` are automatically set at startup by default.\n\n```csharp\npublic static class UnityProviderInitializer\n{\n    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]\n    public static void SetDefaultObservableSystem()\n    {\n        SetDefaultObservableSystem(static ex =\u003e UnityEngine.Debug.LogException(ex));\n    }\n\n    public static void SetDefaultObservableSystem(Action\u003cException\u003e unhandledExceptionHandler)\n    {\n        ObservableSystem.RegisterUnhandledExceptionHandler(unhandledExceptionHandler);\n        ObservableSystem.DefaultTimeProvider = UnityTimeProvider.Update;\n        ObservableSystem.DefaultFrameProvider = UnityFrameProvider.Update;\n    }\n}\n```\n\nA method has been added to convert from UnityEvent to AsObservable. If a CancellationToken is passed, it allows the event source to call for event unsubscription by issuing OnCompleted when Cancel is invoked. For example, if you pass `MonoBehaviour.destroyCancellationToken`, it will be reliably unsubscribed in conjunction with the GameObject's lifecycle.\n\n```csharp\npublic static Observable\u003cUnit\u003e AsObservable(this UnityEngine.Events.UnityEvent unityEvent, CancellationToken cancellationToken = default)\npublic static Observable\u003cT\u003e AsObservable\u003cT\u003e(this UnityEngine.Events.UnityEvent\u003cT\u003e unityEvent, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0 Arg0, T1 Arg1)\u003e AsObservable\u003cT0, T1\u003e(this UnityEngine.Events.UnityEvent\u003cT0, T1\u003e unityEvent, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0 Arg0, T1 Arg1, T2 Arg2)\u003e AsObservable\u003cT0, T1, T2\u003e(this UnityEngine.Events.UnityEvent\u003cT0, T1, T2\u003e unityEvent, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0 Arg0, T1 Arg1, T2 Arg2, T3 Arg3)\u003e AsObservable\u003cT0, T1, T2, T3\u003e(this UnityEngine.Events.UnityEvent\u003cT0, T1, T2, T3\u003e unityEvent, CancellationToken cancellationToken = default)\n```\n\nAdditionally, with extension methods for uGUI, uGUI events can be easily converted to Observables. OnValueChangedAsObservable starts the subscription by first emitting the latest value at the time of subscription. Also when the associated component is destroyed, it emits an OnCompleted event to ensure the subscription is reliably cancelled.\n\n```csharp\npublic static IDisposable SubscribeToText(this Observable\u003cstring\u003e source, Text text)\npublic static IDisposable SubscribeToText\u003cT\u003e(this Observable\u003cT\u003e source, Text text)\npublic static IDisposable SubscribeToText\u003cT\u003e(this Observable\u003cT\u003e source, Text text, Func\u003cT, string\u003e selector)\npublic static IDisposable SubscribeToInteractable(this Observable\u003cbool\u003e source, Selectable selectable)\npublic static Observable\u003cUnit\u003e OnClickAsObservable(this Button button)\npublic static Observable\u003cbool\u003e OnValueChangedAsObservable(this Toggle toggle)\npublic static Observable\u003cfloat\u003e OnValueChangedAsObservable(this Scrollbar scrollbar)\npublic static Observable\u003cVector2\u003e OnValueChangedAsObservable(this ScrollRect scrollRect)\npublic static Observable\u003cfloat\u003e OnValueChangedAsObservable(this Slider slider)\npublic static Observable\u003cstring\u003e OnEndEditAsObservable(this InputField inputField)\npublic static Observable\u003cstring\u003e OnValueChangedAsObservable(this InputField inputField)\npublic static Observable\u003cint\u003e OnValueChangedAsObservable(this Dropdown dropdown)\n```\n\nIn addition to the above, the following `ObserveOn`/`SubscribeOn` methods have been added.\n\n* ObserveOnMainThread\n* SubscribeOnMainThread\n\nWhen using `AddTo(Component / GameObject)` in Unity, it attaches a special component called ObservableDestroyTrigger if gameObject is not active yet, which monitors for destruction. Unity has a characteristic where components that have never been activated do not fire OnDestroy, and the destroyCancellationToken does not get canceled. ObservableDestroyTrigger is designed to monitor for destruction and reliably issue OnDestroy regardless of the active state. It would be wise to use destroyCancellationToken effectively if needed.\n\n```csharp\n// simple pattern\nObservable.EveryUpdate().Subscribe().AddTo(this);\nObservable.EveryUpdate().Subscribe().AddTo(this);\nObservable.EveryUpdate().Subscribe().AddTo(this);\n\n// better performance\nvar d = Disposable.CreateBuilder();\nObservable.EveryUpdate().Subscribe().AddTo(ref d);\nObservable.EveryUpdate().Subscribe().AddTo(ref d);\nObservable.EveryUpdate().Subscribe().AddTo(ref d);\nd.RegisterTo(this.destroyCancellationToken); // Build and Register\n```\n\nYou open tracker window in `Window -\u003e Observable Tracker`. It enables watch `ObservableTracker` list in editor window.\n\n![image](https://github.com/Cysharp/ZLogger/assets/46207/149abca5-6d84-44ea-8373-b0e8cd2dc46a)\n\n* Enable AutoReload(Toggle) - Reload automatically.\n* Reload - Reload view.\n* GC.Collect - Invoke GC.Collect.\n* Enable Tracking(Toggle) - Start to track subscription. Performance impact: low.\n* Enable StackTrace(Toggle) - Capture StackTrace when observable is subscribed. Performance impact: high.\n\nObservable Tracker is intended for debugging use only as enabling tracking and capturing stacktraces is useful but has a heavy performance impact. Recommended usage is to enable both tracking and stacktraces to find subscription leaks and to disable them both when done.\n\n#### `SerializableReactiveProperty\u003cT\u003e`\n\n`ReactiveProperty\u003cT\u003e` can not use on `[SerializeField]`. However you can use `SerializableReactiveProperty\u003cT\u003e` instead.\n\n```csharp\npublic class NewBehaviourScript : MonoBehaviour\n{\n    public SerializableReactiveProperty\u003cint\u003e rpInt;\n    public SerializableReactiveProperty\u003clong\u003e rpLong;\n    public SerializableReactiveProperty\u003cbyte\u003e rpByte;\n    public SerializableReactiveProperty\u003cfloat\u003e rpFloat;\n    public SerializableReactiveProperty\u003cdouble\u003e rpDouble;\n    public SerializableReactiveProperty\u003cstring\u003e rpString;\n    public SerializableReactiveProperty\u003cbool\u003e rpBool;\n    public SerializableReactiveProperty\u003cVector2\u003e rpVector2;\n    public SerializableReactiveProperty\u003cVector2Int\u003e rpVector2Int;\n    public SerializableReactiveProperty\u003cVector3\u003e rpVector3;\n    public SerializableReactiveProperty\u003cVector3Int\u003e rpVector3Int;\n    public SerializableReactiveProperty\u003cVector4\u003e rpVector4;\n    public SerializableReactiveProperty\u003cColor\u003e rpColor;\n    public SerializableReactiveProperty\u003cRect\u003e rpRect;\n    public SerializableReactiveProperty\u003cBounds\u003e rpBounds;\n    public SerializableReactiveProperty\u003cBoundsInt\u003e rpBoundsInt;\n    public SerializableReactiveProperty\u003cQuaternion\u003e rpQuaternion;\n    public SerializableReactiveProperty\u003cMatrix4x4\u003e rpMatrix4x4;\n    public SerializableReactiveProperty\u003cFruitEnum\u003e rpEnum;\n    public SerializableReactiveProperty\u003cFruitFlagsEnum\u003e rpFlagsEnum;\n}\n```\n\n![image](https://github.com/Cysharp/R3/assets/46207/31be9378-846e-4635-8cc6-0b6e3954e918)\n\n#### Triggers\n\nR3 can handle [MonoBehaviour messages](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) with R3.Triggers:\n\nThese can also be handled more easily by directly subscribing to observables returned by extension methods on Component/GameObject. These methods inject ObservableTrigger automatically.\n\n```csharp\nusing R3;\nusing R3.Triggers;\n\n// when using R3.Triggers, Component or GameObject has [MonoBehaviour Messages]AsObservable extension methods.\nthis.OnCollisionEnterAsObservable()\n    .Subscribe(x =\u003e\n    {\n        Debug.Log(\"collision enter\");\n    });\n```\n\n### Godot\n\nGodot support is for Godot 4.x.\n\nThere are some installation steps required to use it in Godot.\n\n1. Install `R3` from NuGet.\n2. Download(or clone git submodule) the repository and move the `src/R3.Godot/addons/R3.Godot` directory to your project.\n3. Enable the `R3.Godot` plugin from the plugins menu.\n\n![image](https://github.com/Cysharp/R3/assets/46207/56bfbb3b-8e7c-4af3-b762-35e4de8a2e83)\n\nGodot support has these TimeProvider and FrameProvider.\n\n```\nGodotTimeProvider.Process\nGodotTimeProvider.PhysicsProcess\n```\n\n```\nGodotFrameProvider.Process\nGodotFrameProvider.PhysicsProcess\n```\n\nautoloaded `FrameProviderDispatcher` set `GodotTimeProvider.Process` and `GodotFrameProvider.Process` as default providers. Additionally, UnhandledException is written to `GD.PrintErr`.\n\nThis is the minimal sample to use R3.Godot.\n\n```csharp\nusing Godot;\nusing R3;\nusing System;\n\npublic partial class Node2D : Godot.Node2D\n{\n    IDisposable subscription;\n\n    public override void _Ready()\n    {\n        subscription = Observable.EveryUpdate()\n            .ThrottleLastFrame(10)\n            .Subscribe(x =\u003e\n            {\n                GD.Print($\"Observable.EveryUpdate: {GodotFrameProvider.Process.GetFrameCount()}\");\n            });\n    }\n\n    public override void _ExitTree()\n    {\n        subscription?.Dispose();\n    }\n}\n```\n\nFor the UI event observe/subscribe extension are also available.\n\n```csharp\npublic static IDisposable SubscribeToLabel(this Observable\u003cstring\u003e source, Label label)\npublic static IDisposable SubscribeToLabel\u003cT\u003e(this Observable\u003cT\u003e source, Label label)\npublic static IDisposable SubscribeToLabel\u003cT\u003e(this Observable\u003cT\u003e source, Label label, Func\u003cT, string\u003e selector)\npublic static Observable\u003cUnit\u003e OnPressedAsObservable(this BaseButton button, CancellationToken cancellationToken = default)\npublic static Observable\u003cbool\u003e OnToggledAsObservable(this BaseButton button, CancellationToken cancellationToken = default)\npublic static Observable\u003cdouble\u003e OnValueChangedAsObservable(this Godot.Range range, CancellationToken cancellationToken = default)\npublic static Observable\u003cstring\u003e OnTextSubmittedAsObservable(this LineEdit lineEdit, CancellationToken cancellationToken = default)\npublic static Observable\u003cstring\u003e OnTextChangedAsObservable(this LineEdit lineEdit, CancellationToken cancellationToken = default)\npublic static Observable\u003cUnit\u003e OnTextChangedAsObservable(this TextEdit textEdit, CancellationToken cancellationToken = default)\npublic static Observable\u003clong\u003e OnItemSelectedAsObservable(this OptionButton optionButton, CancellationToken cancellationToken = default)\n```\n\nAlso, there is support for Sigal.\n\n```csharp\npublic static CancellationToken CancelOnSignal(this GodotObject obj, StringName signalName, bool oneShot = true)\npublic static Observable\u003cUnit\u003e SignalAsObservable(this Node node, StringName signalName)\npublic static Observable\u003cT\u003e SignalAsObservable\u003c[MustBeVariant] T\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3, T4)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6, T7)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7\u003e(this Node node, StringName signalName)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6, T7, T8)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8\u003e(this Node node, StringName signalName)\npublic static Observable\u003cUnit\u003e SignalAsObservable(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003cT\u003e SignalAsObservable\u003c[MustBeVariant] T\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3, T4)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6, T7)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\npublic static Observable\u003c(T0, T1, T2, T3, T4, T5, T6, T7, T8)\u003e SignalAsObservable\u003c[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8\u003e(this GodotObject obj, StringName signalName, CancellationToken cancellationToken = default)\n```\n\nYou can watch subscription status in `Debugger -\u003e ObservableTracker` view.\n\n![image](https://github.com/Cysharp/R3/assets/46207/8b5258a5-8124-4123-a837-79c31427c1d3)\n\n### Stride\n\nR3 extensions for [Stride](https://stride3d.net) game engine.\n\n\u003e PM\u003e Install-Package [R3Extensions.Stride](https://www.nuget.org/packages/R3Extensions.Stride)\n\n#### Usage\n\n1. Reference R3.Stride\n2. add empty Entity by Stride editor\n3. add \"R3/StrideFrameProviderComponent\"\n4. set Stride Frame Provider Component's priority to lower than other scripts which use R3 API\n\nR3Extensions.Stride provides these providers.\n\n* StrideTimeProvider\n* StrideFrameProvider\n\nFor the UI event observe/subscribe extension are also available.\n\n```csharp\npublic static Observable\u003c(object? sender, PropertyChangedArgs\u003cMouseOverState\u003e arg)\u003e MouseOverStateChangedAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e PreviewTouchDownAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e PreviewTouchMoveAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e PreviewTouchUpAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e TouchDownAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e TouchMoveAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e TouchUpAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e TouchEnterAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, TouchEventArgs)\u003e TouchLeaveAsObservable(this UIElement element, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e ClickAsObservable(this ButtonBase btn, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e ValueChangedAsObservable(this Slider slider, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e TextChangedAsObservable(this EditText editText, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e CheckedAsObservable(this ToggleButton toggleButton, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e IndeterminateAsObservable(this ToggleButton button, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e UncheckedAsObservable(this ToggleButton toggleButton, CancellationToken token = default)\npublic static Observable\u003c(object? sender, RoutedEventArgs arg)\u003e OutsideClickAsObservable(this ModalElement modalElement, CancellationToken token = default)\n```\n\nAnd event extensions.\n\n```csharp\npublic static Observable\u003c(object? sender, TrackingCollectionChangedEventArgs arg)\u003e CollectionChangedAsObservable(this ITrackingCollectionChanged hashset, CancellationToken token = default)\npublic static Observable\u003c(object? sender, FastTrackingCollectionChangedEventArgs arg)\u003e CollectionChangedAsObservable\u003cT\u003e(this FastTrackingCollection\u003cT\u003e collection, CancellationToken token = default)\npublic static Observable\u003cT\u003e AsObservable\u003cT\u003e(this EventKey\u003cT\u003e eventKey, CancellationToken token = default)\npublic static Observable\u003cUnit\u003e AsObservable(this EventKey eventKey, CancellationToken token = default)\n```\n\n### MonoGame\n\nR3 extensions for [MonoGame](https://monogame.net) game engine.\n\n\u003e PM\u003e Install-Package [R3Extensions.MonoGame](https://www.nuget.org/packages/R3Extensions.MonoGame)\n\nSet up as follows:\n\n1. Reference R3.MonoGame\n2. Add an instance of `ObservableSystemComponent` to your Game class.\n\n```csharp\npublic class Game1 : Game\n{\n    public Game1()\n    {\n        var observableSystemComponent = new ObservableSystemComponent(this);\n        Components.Add(observableSystemComponent);\n    }\n}\n```\n\nObservableSystemComponent configure the following:\n- Setup TimeProvider and FrameProvider.\n  - Time based operations are replaced with `Game.Update(GameTime)`.\n  - Frame based operations are replaced with `Game.Update(GameTime)`.\n- Set UnhandledExceptionHandler. By default, the unhandled exception handler simply flows to System.Diagnostics.Trace.\n  - If you want to change this, do the following:\n    - ```csharp\n      new ObservableSystemComponent(this, ex =\u003e Console.WriteLine($\"R3 UnhandledException: {ex}\");\n      ```\n\nR3Extensions.MonoGame provides these providers.\n\n* MonoGameTimeProvider\n* MonoGameFrameProvider\n\nAnd provides these custom operators.\n\n```csharp\n// Observe the current GameTime value.\npublic static Observable\u003cGameTime\u003e GameTime(this Observable\u003cUnit\u003e source)\n\n// observe the current GameTime and the value of the source observable.\npublic static Observable\u003c(GameTime GameTime, T Item)\u003e GameTime\u003cT\u003e(this Observable\u003cT\u003e source)\n```\n\n### LogicLooper\n\nR3 extensions for [LogicLooper](https://github.com/Cysharp/LogicLooper/)\n\n\u003e PM\u003e Install-Package [R3Extensions.LogicLooper](https://www.nuget.org/packages/R3Extensions.LogicLooper)\n\nThat supports two special providers.\n\n* LogicLooperFrameProvider\n* LogicLooperTimeProvider\n\n### Blazor\n\nR3 extensions for Blazor.\n\n\u003e PM\u003e Install-Package [R3Extensions.Blazor](https://www.nuget.org/packages/R3Extensions.Blazor)\n\nIf project target is WebAssembly Blazor, import `R3Extensions.BlazorWebAssembly` instead.\n\n\u003e PM\u003e Install-Package [R3Extensions.BlazorWebAssembly](https://www.nuget.org/packages/R3Extensions.BlazorWebAssembly)\n\n```csharp\n// Add this line before Build()\n// for WebAssembly use AddBlazorWebAssemblyR3() instead.\nbuilder.Services.AddBlazorR3();\n\nvar app = builder.Build();\n```\n\nWhen you call `AddBlazorR3/AddBlazorWebAssemblyR3` on IServiceCollection, a TimeProvider corresponding to the request scope is implicitly used and automatically marshaled to the current request. This eliminates the need for InvokeAsync when calling time-related methods within Blazor.\n\n```csharp\npublic partial class Counter : IDisposable\n{\n    int currentCount = 0;\n    IDisposable? subscription;\n\n    protected override void OnInitialized()\n    {\n        subscription = Observable.Interval(TimeSpan.FromSeconds(1))\n            .Subscribe(_ =\u003e\n            {\n                // no needs InvokeAsync\n                currentCount++;\n                StateHasChanged();\n            });\n    }\n\n    public void Dispose()\n    {\n        subscription?.Dispose();\n    }\n}\n```\n\nIn this case, since all default TimeProviders are tied to the request, you must explicitly pass `TimeProvider.System` for executions that are not related to a request.\n\nThere is also a way to utilize R3 in Blazor without using `AddBlazorR3/AddBlazorWebAssemblyR3`. One method is to use `ObserveOnCurrentSynchronizationContext`.\n\n```csharp\nsubscription = Observable.Interval(TimeSpan.FromSeconds(1)) // default TimeProvider is TimeProvider.System\n    .ObserveOnCurrentSynchronizationContext() // uses Blazor RendererSynchronizationContext\n    .Subscribe(_ =\u003e\n    {\n        currentCount++;\n        StateHasChanged();\n    });\n```\n\nAnother method is to inject the TimeProvider. By manually setting up a `SynchronizationContextTimeProvider` tied to the request scope, you can use a custom TimeProvider without changing the default TimeProvider. Also, in this case, it is easy to substitute a `FakeTimeProvider` for unit testing.\n\n```csharp\n// use AddScoped instead of AddBlazorR3\nbuilder.Services.AddScoped\u003cTimeProvider, SynchronizationContextTimeProvider\u003e();\n\nvar app = builder.Build();\n```\n\n```csharp\npublic partial class Counter : IDisposable\n{\n    int currentCount = 0;\n    IDisposable? subscription;\n\n    // Inject scoped TimeProvider manually(in bUnit testing, inject FakeTimeProvider)\n    [Inject]\n    public required TimeProvider TimeProvider { get; init; }\n\n    protected override void OnInitialized()\n    {\n        subscription = Observable.Interval(TimeSpan.FromSeconds(1), TimeProvider)\n            .Subscribe(_ =\u003e\n            {\n                currentCount++;\n                StateHasChanged();\n            });\n    }\n\n    public void Dispose()\n    {\n        subscription?.Dispose();\n    }\n}\n```\n\nOperator Reference\n---\nThe standard operators in ReactiveX follow the behavior described in the [Reactive X Operator documentation](https://reactivex.io/documentation/operators.html).\n\nMethods that accept a Scheduler will take a `TimeProvider`. Additionally, methods that receive a `TimeProvider` have an added method called `***Frame` that accepts a `FrameProvider`.\n\nFor default time based operations that do not take a provider, `ObservableSystem.DefaultTimeProvider` is used, and for frame based operations without provider, `ObservableSystem.DefaultFrameProvider` is used.\n\n### Factory\n\nFactory methods are defined as static methods in the static class `Observable`.\n\n| Name(Parameter) | ReturnType | \n| --- | --- | \n| **CombineLatest**(params `Observable\u003cT\u003e[]` sources) | `Observable\u003cT[]\u003e` | \n| **CombineLatest**(`IEnumerable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT[]\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Func\u003cT1, T2, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Func\u003cT1, T2, T3, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Func\u003cT1, T2, T3, T4, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Func\u003cT1, T2, T3, T4, T5, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Func\u003cT1, T2, T3, T4, T5, T6, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Func\u003cT1, T2, T3, T4, T5, T6, T7, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Observable\u003cT11\u003e` source11, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Observable\u003cT11\u003e` source11, `Observable\u003cT12\u003e` source12, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Observable\u003cT11\u003e` source11, `Observable\u003cT12\u003e` source12, `Observable\u003cT13\u003e` source13, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Observable\u003cT11\u003e` source11, `Observable\u003cT12\u003e` source12, `Observable\u003cT13\u003e` source13, `Observable\u003cT14\u003e` source14, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **CombineLatest**(this `Observable\u003cT1\u003e` source1, `Observable\u003cT2\u003e` source2, `Observable\u003cT3\u003e` source3, `Observable\u003cT4\u003e` source4, `Observable\u003cT5\u003e` source5, `Observable\u003cT6\u003e` source6, `Observable\u003cT7\u003e` source7, `Observable\u003cT8\u003e` source8, `Observable\u003cT9\u003e` source9, `Observable\u003cT10\u003e` source10, `Observable\u003cT11\u003e` source11, `Observable\u003cT12\u003e` source12, `Observable\u003cT13\u003e` source13, `Observable\u003cT14\u003e` source14, `Observable\u003cT15\u003e` source15, `Func\u003cT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult\u003e` resultSelector) | `Observable\u003cTResult\u003e` | \n| **Concat**(params `Observable\u003cT\u003e[]` sources) | `Observable\u003cT\u003e` | \n| **Concat**(`IEnumerable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT\u003e` | \n| **Concat**(this `Observable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT\u003e` | \n| **Create**(`Func\u003cObserver\u003cT\u003e, IDisposable\u003e` subscribe, `Boolean` rawObserver = false) | `Observable\u003cT\u003e` | \n| **Create**(`TState` state, `Func\u003cObserver\u003cT\u003e, TState, IDisposable\u003e` subscribe, `Boolean` rawObserver = false) | `Observable\u003cT\u003e` | \n| **Create**(`Func\u003cObserver\u003cT\u003e, CancellationToken, ValueTask\u003e` subscribe, `Boolean` rawObserver = false) | `Observable\u003cT\u003e` | \n| **Create**(`TState` state, `Func\u003cObserver\u003cT\u003e, TState, CancellationToken, ValueTask\u003e` subscribe, `Boolean` rawObserver = false) | `Observable\u003cT\u003e` | \n| **CreateFrom**(`Func\u003cCancellationToken, IAsyncEnumerable\u003cT\u003e\u003e` factory) | `Observable\u003cT\u003e` | \n| **CreateFrom**(`TState` state, `Func\u003cCancellationToken, TState, IAsyncEnumerable\u003cT\u003e\u003e` factory) | `Observable\u003cT\u003e` | \n| **Defer**(`Func\u003cObservable\u003cT\u003e\u003e` observableFactory, `Boolean` rawObserver = false) | `Observable\u003cT\u003e` | \n| **Empty**() | `Observable\u003cT\u003e` | \n| **Empty**(`TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **Empty**(`TimeSpan` dueTime, `TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **EveryUpdate**() | `Observable\u003cUnit\u003e` | \n| **EveryUpdate**(`CancellationToken` cancellationToken) | `Observable\u003cUnit\u003e` | \n| **EveryUpdate**(`FrameProvider` frameProvider) | `Observable\u003cUnit\u003e` | \n| **EveryUpdate**(`FrameProvider` frameProvider, `CancellationToken` cancellationToken) | `Observable\u003cUnit\u003e` | \n| **EveryValueChanged**(`TSource` source, `Func\u003cTSource, TProperty\u003e` propertySelector, `CancellationToken` cancellationToken = default) | `Observable\u003cTProperty\u003e` | \n| **EveryValueChanged**(`TSource` source, `Func\u003cTSource, TProperty\u003e` propertySelector, `FrameProvider` frameProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cTProperty\u003e` | \n| **EveryValueChanged**(`TSource` source, `Func\u003cTSource, TProperty\u003e` propertySelector, `EqualityComparer\u003cTProperty\u003e` equalityComparer, `CancellationToken` cancellationToken = default) | `Observable\u003cTProperty\u003e` | \n| **EveryValueChanged**(`TSource` source, `Func\u003cTSource, TProperty\u003e` propertySelector, `FrameProvider` frameProvider, `EqualityComparer\u003cTProperty\u003e` equalityComparer, `CancellationToken` cancellationToken = default) | `Observable\u003cTProperty\u003e` | \n| **FromAsync**(`Func\u003cCancellationToken, ValueTask\u003e` asyncFactory, `Boolean` configureAwait = true) | `Observable\u003cUnit\u003e` | \n| **FromAsync**(`Func\u003cCancellationToken, ValueTask\u003cT\u003e\u003e` asyncFactory, `Boolean` configureAwait = true) | `Observable\u003cT\u003e` | \n| **FromEvent**(`Action\u003cAction\u003e` addHandler, `Action\u003cAction\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **FromEvent**(`Action\u003cAction\u003cT\u003e\u003e` addHandler, `Action\u003cAction\u003cT\u003e\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **FromEvent**(`Func\u003cAction, TDelegate\u003e` conversion, `Action\u003cTDelegate\u003e` addHandler, `Action\u003cTDelegate\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **FromEvent**(`Func\u003cAction\u003cT\u003e, TDelegate\u003e` conversion, `Action\u003cTDelegate\u003e` addHandler, `Action\u003cTDelegate\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **FromEventHandler**(`Action\u003cEventHandler\u003e` addHandler, `Action\u003cEventHandler\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cValueTuple\u003cObject, EventArgs\u003e\u003e` | \n| **FromEventHandler**(`Action\u003cEventHandler\u003cTEventArgs\u003e\u003e` addHandler, `Action\u003cEventHandler\u003cTEventArgs\u003e\u003e` removeHandler, `CancellationToken` cancellationToken = default) | `Observable\u003cValueTuple\u003cObject, TEventArgs\u003e\u003e` | \n| **Interval**(`TimeSpan` period, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Interval**(`TimeSpan` period, `TimeProvider` timeProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **IntervalFrame**(`Int32` periodFrame, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **IntervalFrame**(`Int32` periodFrame, `FrameProvider` frameProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Merge**(params `Observable\u003cT\u003e[]` sources) | `Observable\u003cT\u003e` | \n| **Merge**(this `IEnumerable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT\u003e` | \n| **Merge**(this `Observable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT\u003e` | \n| **Never**() | `Observable\u003cT\u003e` | \n| **NextFrame**(`CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **NextFrame**(`FrameProvider` frameProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **ObservePropertyChanged**(this `T` value, `Func\u003cT, TProperty\u003e` propertySelector, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` expr = default) | `Observable\u003cTProperty\u003e` | \n| **ObservePropertyChanged**(this `T` value, `Func\u003cT, TProperty1\u003e` propertySelector1, `Func\u003cTProperty1, TProperty2\u003e` propertySelector2, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` propertySelector1Expr = default, `String` propertySelector2Expr = default) | `Observable\u003cTProperty2\u003e` | \n| **ObservePropertyChanged**(this `T` value, `Func\u003cT, TProperty1\u003e` propertySelector1, `Func\u003cTProperty1, TProperty2\u003e` propertySelector2, `Func\u003cTProperty2, TProperty3\u003e` propertySelector3, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` propertySelector1Expr = default, `String` propertySelector2Expr = default, `String` propertySelector3Expr = default) | `Observable\u003cTProperty3\u003e` | \n| **ObservePropertyChanging**(this `T` value, `Func\u003cT, TProperty\u003e` propertySelector, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` expr = default) | `Observable\u003cTProperty\u003e` | \n| **ObservePropertyChanging**(this `T` value, `Func\u003cT, TProperty1\u003e` propertySelector1, `Func\u003cTProperty1, TProperty2\u003e` propertySelector2, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` propertySelector1Expr = default, `String` propertySelector2Expr = default) | `Observable\u003cTProperty2\u003e` | \n| **ObservePropertyChanging**(this `T` value, `Func\u003cT, TProperty1\u003e` propertySelector1, `Func\u003cTProperty1, TProperty2\u003e` propertySelector2, `Func\u003cTProperty2, TProperty3\u003e` propertySelector3, `Boolean` pushCurrentValueOnSubscribe = true, `CancellationToken` cancellationToken = default, `String` propertySelector1Expr = default, `String` propertySelector2Expr = default, `String` propertySelector3Expr = default) | `Observable\u003cTProperty3\u003e` | \n| **Race**(params `Observable\u003cT\u003e[]` sources) | `Observable\u003cT\u003e` | \n| **Race**(`IEnumerable\u003cObservable\u003cT\u003e\u003e` sources) | `Observable\u003cT\u003e` | \n| **Range**(`Int32` start, `Int32` count) | `Observable\u003cInt32\u003e` | \n| **Range**(`Int32` start, `Int32` count, `CancellationToken` cancellationToken) | `Observable\u003cInt32\u003e` | \n| **Repeat**(`T` value, `Int32` count) | `Observable\u003cT\u003e` | \n| **Repeat**(`T` value, `Int32` count, `CancellationToken` cancellationToken) | `Observable\u003cT\u003e` | \n| **Return**(`T` value) | `Observable\u003cT\u003e` | \n| **Return**(`T` value, `TimeProvider` timeProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **Return**(`T` value, `TimeSpan` dueTime, `TimeProvider` timeProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **Return**(`Unit` value) | `Observable\u003cUnit\u003e` | \n| **Return**(`Boolean` value) | `Observable\u003cBoolean\u003e` | \n| **Return**(`Int32` value) | `Observable\u003cInt32\u003e` | \n| **ReturnFrame**(`T` value, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **ReturnFrame**(`T` value, `FrameProvider` frameProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **ReturnFrame**(`T` value, `Int32` dueTimeFrame, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **ReturnFrame**(`T` value, `Int32` dueTimeFrame, `FrameProvider` frameProvider, `CancellationToken` cancellationToken = default) | `Observable\u003cT\u003e` | \n| **ReturnOnCompleted**(`Result` result) | `Observable\u003cT\u003e` | \n| **ReturnOnCompleted**(`Result` result, `TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **ReturnOnCompleted**(`Result` result, `TimeSpan` dueTime, `TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **ReturnUnit**() | `Observable\u003cUnit\u003e` | \n| **Throw**(`Exception` exception) | `Observable\u003cT\u003e` | \n| **Throw**(`Exception` exception, `TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **Throw**(`Exception` exception, `TimeSpan` dueTime, `TimeProvider` timeProvider) | `Observable\u003cT\u003e` | \n| **Timer**(`TimeSpan` dueTime, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Timer**(`DateTimeOffset` dueTime, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Timer**(`TimeSpan` dueTime, `TimeSpan` period, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Timer**(`DateTimeOffset` dueTime, `TimeSpan` period, `CancellationToken` cancellationToken = default) | `Observable\u003cUnit\u003e` | \n| **Timer**(`TimeSpa","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FR3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCysharp%2FR3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FR3/lists"}