R3Events 0.3.0

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

R3Events

CI NuGet Downloads

A C# source generator that automatically bridges .NET events and R3 reactive programming — annotate a static partial class with [R3Event<T>] and get AsObservable() extension methods for every public event on T with zero boilerplate.

NuGet: R3Events

Install-Package R3Events

Introduction

R3Events automatically generates AsObservable() extension methods for all public events on a target type, allowing you to seamlessly convert .NET events into R3 observables.

For example, suppose you have a class with events:

using System.ComponentModel;

class MyControl
{
    public event EventHandler? Click;
    public event CancelEventHandler? BeforeClose;
}

Simply create a static partial class with the R3Event attribute:

using R3Events;

[R3Event(typeof(MyControl))]
static partial class MyControlExtensions
{
}

Or when using C# 11 or later, you can use the generic attribute syntax:

using R3Events;

[R3Event<MyControl>]
static partial class MyControlExtensions
{
}

The generator will automatically create extension methods:

// <auto-generated />
static partial class MyControlExtensions
{
    /// <summary>
    /// Returns an Observable for <c>Click</c>.
    /// </summary>
    public static Observable<Unit> ClickAsObservable(this MyControl instance, CancellationToken cancellationToken = default)
    {
        var rawObservable = Observable.FromEventHandler(
            h => instance.Click += h,
            h => instance.Click -= h,
            cancellationToken
        );
        return rawObservable.AsUnitObservable();
    }

    /// <summary>
    /// Returns an Observable for <c>BeforeClose</c>.
    /// </summary>
    public static Observable<CancelEventArgs> BeforeCloseAsObservable(this MyControl instance, CancellationToken cancellationToken = default)
    {
        var rawObservable = Observable.FromEvent<CancelEventHandler, (object?, CancelEventArgs Args)>(
            h => new CancelEventHandler((s, e) => h((s, e))),
            h => instance.BeforeClose += h,
            h => instance.BeforeClose -= h,
            cancellationToken
        );
        return rawObservable.Select(ep => ep.Args);
    }
}

Now you can use these events as observables:

var control = new MyControl();

// Subscribe to Click event as Observable
control.ClickAsObservable()
    .Subscribe(_ => Console.WriteLine("Clicked!"));

// Subscribe to BeforeClose event with filtering
control.BeforeCloseAsObservable()
    .Where(args => !args.Cancel)
    .Subscribe(args => Console.WriteLine("Closing..."));

Table of Contents

R3EventAttribute

When referencing the R3Events package, it generates an internal R3EventAttribute:

namespace R3Events
{
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    internal sealed class R3EventAttribute : Attribute
    {
        public R3EventAttribute(Type type) { ... }
        public Type Type { get; }
    }
}

When using C# 11 or later, the generator additionally creates a generic variant:

namespace R3Events
{
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    internal sealed class R3EventAttribute<T> : Attribute
    {
    }
}

You can attach this attribute to a static partial class. The generator will scan the specified target type for all public, non-static events and generate corresponding AsObservable() extension methods.

// C# 10 or earlier
[R3Event(typeof(Button))]
static partial class ButtonExtensions { }

// C# 11 or later
[R3Event<Button>]
static partial class ButtonExtensions { }

The attributed class must be:

  • static
  • partial
  • Not nested within another type
  • Not generic

If any of these requirements are not met, the generator will emit a diagnostic error.

Generated Extension Methods

The generator creates extension methods for all public, non-static events on the target type. The method naming convention is {EventName}AsObservable.

EventHandler support

For System.EventHandler (non-generic), the generated observable uses R3.Unit:

public event EventHandler? Click;

// Generates:
public static Observable<Unit> ClickAsObservable(
    this TargetType instance,
    CancellationToken cancellationToken = default)

Generic EventHandler support

For generic event handlers like EventHandler<T>, the generated observable uses the event args type:

public event EventHandler<MouseEventArgs>? MouseMove;

// Generates:
public static Observable<MouseEventArgs> MouseMoveAsObservable(
    this TargetType instance,
    CancellationToken cancellationToken = default)

Custom delegate support

For custom delegates, the generator examines the delegate signature and uses the last parameter as the observable element type:

public delegate void CustomEventHandler(object sender, CustomEventArgs e);
public event CustomEventHandler? CustomEvent;

// Generates:
public static Observable<CustomEventArgs> CustomEventAsObservable(
    this TargetType instance,
    CancellationToken cancellationToken = default)

Diagnostics

R3W001 — Prefer generic R3EventAttribute<T>

Severity: Warning
Applies to: C# 11 or later

When C# 11 or later is in use, the generator also emits the generic R3EventAttribute<T>. Using the non-generic [R3Event(typeof(T))] form while a generic alternative is available triggers warning R3W001 at the attribute site:

R3W001  Type 'MyNamespace.MyClassExtensions' uses R3EventAttribute(typeof(T)).
        Consider using R3EventAttribute<T> instead, which is available in C# 11 and later.
Quick Fix

A code fix (quick action) is available to migrate automatically. Applying it replaces:

// Before
[R3Event(typeof(MyClass))]
internal static partial class MyClassExtensions { }

// After (applied automatically by the code fix)
[R3Event<MyClass>]
internal static partial class MyClassExtensions { }

Any namespace qualification is preserved:

// R3Events.R3Event(typeof(T)) → R3Events.R3Event<T>
[R3Events.R3Event(typeof(MyClass))]
internal static partial class MyClassExtensions { }
// becomes:
[R3Events.R3Event<MyClass>]
internal static partial class MyClassExtensions { }

Note: R3W001 is only emitted when the project's C# language version is 11 or later. For C# 8-10, the non-generic [R3Event(typeof(T))] form is the only option and no warning is produced.

Requirements

  • .NET Standard 2.0 or later
  • R3 package
  • C# 8.0 or later (generated code includes nullable reference type syntax such as ?)
  • C# 11 or later (only required for generic attribute syntax R3Event<T>)
Install-Package R3
Install-Package R3Events

Important: While R3Events does not have a package dependency on R3, the generated code calls R3 APIs (Observable.FromEvent, Observable.FromEventHandler, etc.). Therefore, you must install the R3 package in your project for the generated code to compile successfully.

License

This library is under the MIT License.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 465 3/2/2026
0.2.0 91 2/22/2026
0.1.0 93 2/19/2026