R3Events 0.3.0
dotnet add package R3Events --version 0.3.0
NuGet\Install-Package R3Events -Version 0.3.0
<PackageReference Include="R3Events" Version="0.3.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="R3Events" Version="0.3.0" />
<PackageReference Include="R3Events"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add R3Events --version 0.3.0
#r "nuget: R3Events, 0.3.0"
#:package R3Events@0.3.0
#addin nuget:?package=R3Events&version=0.3.0
#tool nuget:?package=R3Events&version=0.3.0
R3Events
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:
staticpartial- 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.
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.