Reactive Extensions (Rx) - Part 2 - Wrapping C# Events

Reactive Extensions
  1. Reactive Extensions (Rx) - Replacing C# Events
  2. Reactive Extensions (Rx) - Wrapping C# Events
  3. Reactive Extensions (Rx) - Naming Conventions
  4. Reactive Extensions (Rx) - Replacing Timers
  5. Reactive Extensions (Rx) - Awaiting Observables
  6. Reactive Extensions (Rx) - Task ToObservable
  7. Reactive Extensions (Rx) - Sample Events
  8. Reactive Extensions (Rx) - Timeouts

Sometimes it is not possible to replace a C# event with a Reactive Extensions (Rx) event entirely. This is usually because we are implementing an interface which has a C# event and we don't own the interface.

However, as I'll show in this post, its possible to create IObservable<T> wrappers for C# events and even to hide the C# events entirely from consumers of the class.

The method of wrapping C# events depends on the type of event handler used. Below are the three type of event handler and the method of wrapping them with an observable event.

Wrapping an EventHandler C# Event

The FromEventPattern method is used to wrap the event. Notice we have to specify delegates for subscribing (+=) and unsubscribing (-=) to the event.

public event EventHandler BunnyRabbitsAttack;

public IObservable<object> WhenBunnyRabbitsAttack
{
    get
    {
        return Observable
            .FromEventPattern(
                h => this.BunnyRabbitsAttack += h,
                h => this.BunnyRabbitsAttack -= h);
    }
}

Wrapping an EventHandler C# Event

This example is much the same as the last, except we have to deal with the event arguments. The FromEventPattern method returns an EventPattern<T> object, which contains the sender and the event arguments. We're only interested in the contents of the event arguments, so we use a Select to return just the BunnyRabbits property.

public event EventHandler<BunnyRabbitsEventArgs> BunnyRabbitsAttack;

public IObservable<BunnyRabbits> WhenBunnyRabbitsAttack
{
    get
    {
        return Observable
            .FromEventPattern<BunnyRabbitsEventArgs>(
                h => this.BunnyRabbitsAttack += h,
                h => this.BunnyRabbitsAttack -= h)
            .Select(x => x.EventArgs.BunnyRabbits);
    }
}

Wrapping a Custom Event Handler C# Event

Some C# events use a custom event handler. In this case we have to specify the type of the event handler as a generic argument in the FromEventPattern method.

public event BunnyRabbitsEventHandler BunnyRabbitsAttack;

public IObservable<BunnyRabbits> WhenBunnyRabbitsAttack
{
    get
    {
        return Observable
            .FromEventPattern<BunnyRabbitsEventHandler, BunnyRabbitsEventArgs>(
                h => this.BunnyRabbitsAttack += h,
                h => this.BunnyRabbitsAttack -= h)
            .Select(x => x.EventArgs.BunnyRabbits);
    }
}

Hiding Existing Events Using Explicit Interface Implementation

The disadvantage of the above approach is that we now have two ways to access our event. One with the old style C# event and the other with our new Reactive Extensions event. With a bit of trickery we can hide the C# event in some cases.

The INotifyPropertyChanged interface is very commonly used by XAML developers. It has a single event called PropertyChanged. To hide the PropertyChanged C# event we can explicitly implement the interface (Click here for details on implicit versus explicit implementations of interfaces). Secondly, we wrap the event as we did before.

Now the PropertyChanged C# event can only be accessed by first casting the object to INotifyPropertyChanged (Binding in XAML languages, which uses this interface continues to work). Our new Reactive Extensions observable event is now the default method of subscribing for property changed events.

public abstract class NotifyPropertyChanges : INotifyPropertyChanged
{
    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add { this.propertyChanged += value; }
        remove { this.propertyChanged -= value; }
    }

    private event PropertyChangedEventHandler propertyChanged;

    public IObservable<string> WhenPropertyChanged
    {
        get
        {
            return Observable
                .FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                    h => this.propertyChanged += h,
                    h => this.propertyChanged -= h)
                .Select(x => x.EventArgs.PropertyName);
        }
    }

    protected void OnPropertyChanged(string propertyName) =>
        this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Summing Up

So it may not always be possible to get rid of, dare I say it legacy C# events but we can certainly wrap them with Reactive Extension observables and even hide them altogether.

Web Mentions

What's this?

0 Replies

GitHub Comments

RehanSaeed

Davide Cologni Davide Cologni commented on 2018-01-02 22:25:55

Hi I found this tutorial very useful, I want suggest to use the dot conditional operator (?.) in the protected method that wrap event wo explicit checking if handler is null in this way:

this.propertyChange?.Invoke(this, new EventArgsBlaBla());

With expression body function is very concise and elegant :^)

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-01-03 10:20:00

Hi I found this tutorial very useful, I want suggest to use the dot conditional operator (?.) in the protected method that wrap event wo explicit checking if handler is null in this way:

this.propertyChange?.Invoke(this, new EventArgsBlaBla());

With expression body function is very concise and elegant :^)

I wrote the blog post before the null conditional operator was available but I've now updated the post, thanks!

RehanSaeed

George Papadopoulos George Papadopoulos commented on 2019-03-06 21:40:11

Hi there, is it possible to give us an example of how the abstract class NotifyPropertyChanges is used? Thanks In advance.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2019-03-08 10:02:44

Hi there, is it possible to give us an example of how the abstract class NotifyPropertyChanges is used? Thanks In advance.

I wrote a series of blog posts you can look at here.