Convert events without EventArgs using Observable.FromEvent

I am trying to convert the following event to IObservable :

 public delegate void _dispSolutionEvents_OpenedEventHandler(); event _dispSolutionEvents_OpenedEventHandler Opened; 

The event comes from the library, so I cannot change it. The overload of IObservable.FromEvent , which should do this, has the following signature:

 public static IObservable<Unit> FromEvent ( Action<Action> addHandler , Action<Action> removeHandler ) 

So, I tried to convert the event as follows:

 var opened = Observable.FromEvent ( h => _SolutionEvents.Opened += h , h => _SolutionEvents.Opened -= h ); 

But the compiler does not like _SolutionEvents.Opened += h and _SolutionEvents.Opened += h , because

Cannot implicitly convert type 'System.Action' to 'EnvDTE._dispSolutionEvents_OpenedEventHandler'.

I don’t think I can just say _SolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(h) , because deletion will not work, because I have another instance, right?

There is another overload of Observable.FromEvent with the following signature:

 public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs> ( Func<Action<TEventArgs>, TDelegate> conversion , Action<TDelegate> addHandler , Action<TDelegate> removeHandler ) 

This allows you to convert the action to an event handler, but it seems to work only with TEventArgs .

Is Rx missing the appropriate overload or am I missing something?

+7
c # events system.reactive
source share
2 answers

It turns out it's very easy to use the FromEvent pattern.

Just do the following:

 var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>( h => () => h(Unit.Default), h => _SolutionEvents.Opened += h, h => _SolutionEvents.Opened -= h); 

I checked the observable with this code:

 void Main() { var _SolutionEvents = new Foo(); var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(h => () => h(Unit.Default), h => _SolutionEvents.Opened += h, h => _SolutionEvents.Opened -= h); opened.Subscribe(x => Console.WriteLine("Opened")); _SolutionEvents.OnOpened(); } public delegate void _dispSolutionEvents_OpenedEventHandler(); public class Foo { public event _dispSolutionEvents_OpenedEventHandler Opened; public void OnOpened() { this.Opened?.Invoke(); } } 

It produces the following expected result:

  Opened

It is worth noting that there is no IObservable interface, but only IObservable<T> , so you have to return something. The trick here is to convert delegate void _dispSolutionEvents_OpenedEventHandler() to IObservable<Unit> to make it work and that does h => () => h(Unit.Default) .

+9
source share

Here you are faced with a type problem. The type _dispSolutionEvents_OpenedEventHandler not Action . This is similar to an Action type, but it is not an Action type.

IMO this event does not comply with the .NET standards for events. Typically, the delegate will conform to the pattern of accepting the sender parameter object and the EventArg subclass for the second parameter.

t

 public delegate void _dispSolutionEvents_OpenedEventHandler(object sender, EventArgs e); 

If you try to simply attach the Action to the event, you will find that this will not work either.

 Action onOpened = ()=>Console.WriteLine("Opened"); _SolutionEvents.Opened += onOpened; //CS0029 Cannot implicitly convert type 'System.Action' to '_dispSolutionEvents_OpenedEventHandler' 

The compiler is smart enough to make a type conclusion if you do;

 _SolutionEvents.Opened+= () => Console.WriteLine("Opened"); 

but when you use Rx, you are already cast into the Action type, so they really reverted to the previous problem above.

If the library owner was good, the event will follow the regular sender / event pattern of Args. Otherwise, they would at least indicate the delegate as just an Action instead of their own parallelless, invalid method .: - /

So, since your event does not match the standard .NET templates, you will need to give Rx even more hands (blame the library provider, not Rx).

You can struggle with the FromEvent / FromEventPattern , but since your library is not in the spirit of the event, I would suggest just going to the simple use of Observable.Create , which at least makes the code obvious what is happening and should allow the next user to better understand this is.

 Observable.Create<Unit>(obs => { _dispSolutionEvents_OpenedEventHandler handler = () => obs.OnNext(Unit.Default); _SolutionEvents.Opened += handler; return System.Reactive.Disposables.Disposable.Create(() => _SolutionEvents.Opened -= handler); }); 
+4
source share

All Articles