I'm trying to implement WeakEventManager in PCL using the Reactive library.
So the point is that it keeps a weak reference for the subscriber and each time event fires - it gets the delegate of the subscriber and fires that, but if he couldn't get an object from weak reference, then it disposes the link to the delegate.
The problem is that after a short amount of time, the weak reference returns null (but the subscriber is still alive) and after that disposing of the link is being performed. So my question is why this is happening and how to fix that?
Here how it looks like: (Look at the note in the code)
private static IDisposable InternalSubscribeWeakly<TEventPattern, TEvent>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, Action<TEvent, TEventPattern> onNext)
where TEvent : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// Is the delegate alive?
var Weak_onNextReferance = new WeakReference(Weak_onNext);
//This is a link for that event, so if you want to unsubscribe from event you have to dispose this object
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
//So the library keeps weak reference for this object and each time event fired it tries to get that object
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (current_onNext != null)
{
//If the object was found, it uses the delegate that subscriber provided and fires the event
onNext(current_onNext, item);
}
else
{
//If the object is not found it disposes the link
//NOTE: For some reasons after a short amount of time it can't get a reference from the WeakReference, however the subscriber is still alive
subscription.Dispose();
}
});
return subscription;
}
And then here is how I'm subscribing using that manager:
private void NoLeakWindow_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= NoLeakWindow_Loaded;
this.ObserveOn<Window, ElapsedEventHandler, ElapsedEventArgs>(h => (o, s) => h(o, s),
r => MainWindow.EPublisher.EventTimer.Elapsed += r,
r => MainWindow.EPublisher.EventTimer.Elapsed -= r)
.SubscribeWeakly(EventTimer_Elapsed);
this.ObserveOn<Window, ElapsedEventHandler, ElapsedEventArgs>(
h => (o, s) => h(o, s),
r => MainWindow.EPublisher.EventTimer.Elapsed += r,
r => MainWindow.EPublisher.EventTimer.Elapsed -= r)
.SubscribeWeakly(EventTimer_Elapsed2);
}
private void EventTimer_Elapsed(EventPattern<ElapsedEventArgs> e)
{
MessageBox.Show("EventTimer_Elapsed By Timer");
}
private void EventTimer_Elapsed2(EventPattern<ElapsedEventArgs> e)
{
MessageBox.Show("EventTimer2_Elapsed2 By Timer2");
}
And my event publisher:
public class EventPublisher
{
public Timer EventTimer = new Timer(3000);
public Timer EventTimer2 = new Timer(2700);
public event EventHandler<EventArgs> TimeElapsed;
public EventPublisher()
{
EventTimer.Start();
EventTimer2.Start();
}
}
And finally the WeakEventManager class full code:
/// <summary>
/// Static Class that holds the extension methods to handle events using weak references.
/// This way we do not need to worry about unregistered the event handler.
/// </summary>
public static class WeakEventManager
{
/// <summary>
/// Creates Observable for subscribing to it's event
/// </summary>
/// <typeparam name="T">The type of the T.</typeparam>
/// <typeparam name="TDelegate">The type of the T delegate.</typeparam>
/// <typeparam name="TArgs">The type of the T args.</typeparam>
/// <param name="subscriber">The subscriber</param>
/// <param name="converter">The converter.</param>
/// <param name="add">The add</param>
/// <param name="remove">The remove</param>
/// <returns>IObservable</returns>
public static IObservable<EventPattern<TArgs>> ObserveOn<T, TDelegate, TArgs>(this T subscriber, Func<EventHandler<TArgs>, TDelegate> converter, Action<TDelegate> add, Action<TDelegate> remove)
where T : class
{
return Observable.FromEventPattern<TDelegate, TArgs>(
converter,
add,
remove);
}
/// <summary>
/// Subscribe's action to event
/// </summary>
/// <typeparam name="T">The type of the T.</typeparam>
/// <param name="observable">The observable</param>
/// <param name="onNext">The action</param>
/// <returns></returns>
public static IDisposable SubscribeWeakly<T>(this IObservable<T> observable, Action<T> onNext) where T : class
{
IDisposable Result = null;
WeakSubscriberHelper<T> SubscriptionHelper = new WeakSubscriberHelper<T>(observable, ref Result, onNext);
return Result;
}
private class WeakSubscriberHelper<T> where T : class
{
public WeakSubscriberHelper(IObservable<T> observable, ref IDisposable Result, Action<T> eventAction)
{
Result = observable.InternalSubscribeWeakly(eventAction, WeakSubscriberHelper<T>.StaticEventHandler);
}
public static void StaticEventHandler(Action<T> subscriber, T item)
{
subscriber(item);
}
}
private static IDisposable InternalSubscribeWeakly<TEventPattern, TEvent>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, Action<TEvent, TEventPattern> onNext)
where TEvent : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// Is the delegate alive?
var Weak_onNextReferance = new WeakReference(Weak_onNext);
//This is a link for that event, so if you want to unsubscribe from event you have to dispose this object
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
//So the library keeps weak reference for this object and each time event fired it tries to get that object
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (current_onNext != null)
{
//If the object was found, it uses the delegate that subscriber provided and fires the event
onNext(current_onNext, item);
}
else
{
//If the object is not found it disposes the link
//NOTE: For some reasons after a short amount of time it can't get a reference from the WeakReference, however the subscriber is still alive
subscription.Dispose();
}
});
return subscription;
}
public static IDisposable SubscribeWeakly<T, TWeakClass>(this IObservable<T> observable, TWeakClass WeakClass, Action<T> onNext) where T : class where TWeakClass : class
{
IDisposable Result = null;
WeakClassSubscriberHelper<T> SubscriptionHelper = new WeakClassSubscriberHelper<T>(observable, WeakClass, ref Result, onNext);
return Result;
}
private class WeakClassSubscriberHelper<T> where T : class
{
public WeakClassSubscriberHelper(IObservable<T> observable, object WeakClass, ref IDisposable Result, Action<T> eventAction)
{
Result = observable.InternalSubscribeWeaklyToClass(eventAction, WeakClass, WeakClassSubscriberHelper<T>.StaticEventHandler);
}
public static void StaticEventHandler(Action<T> subscriber, T item)
{
subscriber(item);
}
}
private static IDisposable InternalSubscribeWeaklyToClass<TEventPattern, TEvent, TClass>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, TClass WeakClass, Action<TEvent, TEventPattern> onNext)
where TEvent : class where TClass : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// The class instance could live in a differnt
// place than the eventhandler. If either one is null,
// terminate the subscribtion.
var WeakClassReference = new WeakReference(WeakClass);
var Weak_onNextReferance = new WeakReference(Weak_onNext);
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
var currentWeakClass = WeakClassReference.Target as TClass;
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (currentWeakClass != null && current_onNext != null)
{
onNext(current_onNext, item);
}
else
{
subscription.Dispose();
}
});
return subscription;
}
}