Reactive Extensions (.NET)

repository·main·Indexed 27 days ago

https://github.com/dotnet/reactive

A set of libraries for composing asynchronous and event-based programs using observable sequences and LINQ-style operators. Includes Rx.NET for event-driven programming with IObservable<T>, AsyncRx.NET for IAsyncObservable<T> with async/await support, Interactive Extensions (Ix.NET) for extended LINQ operators, and System.Linq.Async for LINQ support over IAsyncEnumerable<T>.

Tokens
66.6K
Snippets
129
Records
308
Agent score
93%

What's inside dotnet-reactive

  1. Overview of Reactive Extensions libraries

    main

    The dotnet/reactive repository contains four conceptually related libraries focused on LINQ over sequences. Depending on your needs, you can use one or more of the following:

    • Reactive Extensions for .NET (Rx.NET): A library for event-driven programming using a composable, declarative model. It uses IObservable<T> to represent live data streams.
    • AsyncRx.NET (Experimental Preview): An implementation of Rx for IAsyncObservable<T> that provides deeper async/await support for observers.
    • Interactive Extensions (Ix.NET): Provides extended LINQ operators for both IAsyncEnumerable<T> and IEnumerable<T>. It acts as an extension to LINQ to Objects.
    • LINQ for IAsyncEnumerable (System.Linq.Async): Implements standard LINQ operators specifically for IAsyncEnumerable<T>.
  2. Understand the Rx.NET package split and UI framework dependencies

    main

    Rx.NET is transitioning its architecture to separate core Reactive Extensions functionality from UI-framework-specific implementations (like WPF, Windows Forms, and UWP).

    In older versions (v6), the System.Reactive package contained UI-specific types (e.g., DispatcherScheduler), which forced applications to acquire transitive dependencies on WPF or Windows Forms even if they only needed core Rx functionality.

    To avoid 'bloat' and unwanted framework dependencies, developers should look for UI-specific functionality in dedicated packages rather than relying on the core System.Reactive package for UI-related schedulers or types.

  3. Combine multiple data sources using sequence composition

    main

    Reactive Extensions (Rx) allows you to combine multiple observable streams to create complex queries across different data sources (e.g., price feeds, sensor networks, or news feeds). You can restructure streams by partitioning data, processing it, and then recombining it.

    Common strategies for combining sequences include:

    • Integrated Data: Consuming all inputs as a single deluge of data.
    • Sequential Data: Consuming one sequence at a time.
    • Paired Data: Pairing values from two sources to be processed together.
    • First-Response: Consuming data from the first source that responds to a request.

    While SelectMany is a fundamental operator for combining streams, Rx provides specialized combination operators and overloads for operators like TakeUntil and Buffer to solve specific composition problems more efficiently.

  4. Understanding the System.Reactive package split strategy

    main

    The System.Reactive package is undergoing a structural change to move UI-framework-specific code (like WPF and Windows Forms support) out of the core library. This is being done to improve the maintainability of the core Rx library and to prevent UI-specific dependencies from being visible to the compiler in non-UI contexts.

    Key implications for users:

    • Source-level breaking changes: To remove UI-specific code from the core System.Reactive package, certain APIs that were previously available may be removed from the public API surface. This will result in source-level breaking changes for code that relies on UI-framework-specific functionality within System.Reactive.
    • Binary compatibility vs. Source compatibility: While the team aims to minimize binary-level breaking changes (using techniques like the ref\ vs lib\ trick), users should expect that code using UI-specific features may require updates to compile against newer versions of Rx.
    • Potential for new packages: UI-specific functionality is expected to be moved into separate, dedicated NuGet packages to allow users to opt-in to the features they need without bloating the core library.
  5. Understand the role of Schedulers in Rx

    main

    Schedulers are responsible for three core functions in Reactive Extensions:

    1. Determining the context: Deciding which thread or execution context to use for work.
    2. Deciding when to execute: Determining if work should happen immediately, after a delay, or at a specific time.
    3. Keeping track of time: Managing time-based operations and providing an abstraction for time (which allows for time virtualization in tests).

    Most operators use a default scheduler (often CurrentThreadScheduler) unless explicitly provided.

  6. Understand the Ix.NET library components

    main

    The Ix.NET project provides several libraries for functional and asynchronous programming in .NET:

    • System.Interactive: The original library providing extension methods for IEnumerable<T>.
    • System.Interactive.Async: Provides similar functionality to System.Interactive but specifically for IAsyncEnumerable<T>.
    • System.Linq.Async: Provides a full LINQ implementation for IAsyncEnumerable<T>, including standard operators like Where, Single, and GroupBy that are not built into the .NET runtime for asynchronous streams.
  7. Why use IObservable<T> instead of System.IO.Stream

    main

    When modeling event streams, System.IO.Stream is often an unsuitable abstraction compared to IObservable<T>. Key limitations of System.IO.Stream include:

    • Byte-oriented vs. Type-safe: Stream is designed for bytes. It cannot natively represent complex types like int or float without manual parsing. IObservable<T> uses generics to provide type safety for any data type.
    • Buffering Delays: Many Stream implementations use buffering (e.g., waiting for 4096 bytes) to improve OS efficiency. This introduces latency that is unacceptable for real-time event processing (like UI inputs or financial data).
    • Unpredictable Read Sizes: Standard Read or ReadAsync methods may return fewer bytes than requested due to underlying hardware or network constraints. While .NET 7.0 introduced ReadExactly and ReadExactlyAsync to mitigate this, older versions require manual handling.
    • Lack of Multi-subscriber Support: Stream does not provide a standard model for multiple observers to consume the same data stream simultaneously.
    • Implementation Complexity: Implementing a custom Stream requires overriding ten abstract members (5 properties and 5 methods), whereas System.Reactive provides much simpler patterns for creating event sources.
  8. Understand the Monadic Foundation of Rx.NET

    main

    Rx.NET (and LINQ) is built on the mathematical concept of Monads. In a practical programming context, a monad is a way to represent a container of items (like IEnumerable<T> or IObservable<T>).

    Understanding monads is important because their mathematical properties enable Rx operators to be composed freely. If a .NET type is a monad, it is a candidate for LINQ-style implementations. For Rx, IObservable<T> is a monad.

  9. Identify the mathematical foundations of Rx composition

    main

    The ability to freely compose LINQ operators in Rx is not accidental; it is underpinned by mathematical concepts from category theory. Understanding these concepts explains why Rx components integrate so reliably. The three primary mathematical concepts that standard LINQ operators can be expressed in terms of are:

    • Monads
    • Catamorphisms
    • Anamorphisms
  10. Understand the core Rx types: IObservable<T> and IObserver<T>

    main
    The fundamental building blocks of Reactive Extensions (Rx) are IObservable<T> and IObserver<T>. While standard LINQ (like LINQ to Objects) operates on IEnumerable<T> to query data at rest (collections that can be enumerated), Rx uses IObservable<T> to define queries over data in motion (live event streams).
  11. Understand the core concepts of Rx.NET

    main

    Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs. It is built on three primary pillars:

    1. Observables: Represent asynchronous data streams using the IObservable<T> interface.
    2. LINQ Operators: Query asynchronous data streams using LINQ-style extension methods (e.g., filtering, projecting, aggregating, and time-based operations).
    3. Schedulers: Parameterize concurrency within the asynchronous data streams.

    Rx allows you to subscribe to an event stream using the IObserver<T> interface, which is notified by the IObservable<T> whenever an event occurs. It handles cancellation, exceptions, and synchronization gracefully through its extension methods.

  12. Understand Rx operator categories (Anamorphisms, Bind, Catamorphisms)

    main

    Rx operators can be categorized by how they interact with the observable sequence based on functional programming concepts:

    • Anamorphisms (Enter the sequence): Operations that create an IObservable<T> from a source. Examples include Generate, Range, and Return.
    • Bind (Modify the sequence): Operations that transform an IObservable<T1> into an IObservable<T2>. Examples include SelectMany, Select, and Where.
    • Catamorphisms (Leave the sequence): Operations that logically move from IObservable<T1> to a single value T2 (though in practice they often return an IObservable<T2> that produces a single value). Examples include Aggregate, Sum, Min, and Max.