R3Events 0.4.2
dotnet add package R3Events --version 0.4.2
NuGet\Install-Package R3Events -Version 0.4.2
<PackageReference Include="R3Events" Version="0.4.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="R3Events" Version="0.4.2" />
<PackageReference Include="R3Events"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add R3Events --version 0.4.2
#r "nuget: R3Events, 0.4.2"
#:package R3Events@0.4.2
#addin nuget:?package=R3Events&version=0.4.2
#tool nuget:?package=R3Events&version=0.4.2
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, R3EventAttribute is provided as a public type from the R3Events.Attributes assembly:
namespace R3Events
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class R3EventAttribute : Attribute
{
public R3EventAttribute(Type type) { ... }
public Type Type { get; }
}
}
When using C# 11 or later, a generic variant is also available:
namespace R3Events
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public 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
R3I001 — Prefer generic R3EventAttribute<T>
Severity: Info
Applies to: C# 11 or later
When C# 11 or later is in use, the generic R3EventAttribute<T> is available from the shared attributes assembly. Using the non-generic [R3Event(typeof(T))] form while a generic alternative is available triggers info diagnostic R3I001 at the attribute site:
R3I001 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: R3I001 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 info diagnostic 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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- 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.