Próbuję zaimplementować WeakEventManager w PCL za pomocą biblioteki reaktywnej.PCL WeakEventManager z rozszerzeń reaktywnych wywołuje zdarzenie w ciągu 3 - 7 minut
Chodzi o to, że zachowuje słabe odniesienie dla subskrybenta i za każdym razem, gdy zdarzenie się uruchamia - pobiera delegata subskrybenta i odpala to, ale jeśli nie mógł uzyskać obiektu ze słabego odniesienia, to link do delegata.
Problem polega na tym, że po niedługim czasie słaba referencja zwraca wartość null (ale subskrybent wciąż żyje), po czym następuje unieszkodliwienie łącza. Moje pytanie brzmi: dlaczego tak się dzieje i jak to naprawić?
Oto jak to wygląda: (Spójrz na notatki w kodzie)
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;
}
I to tutaj jest jak ja subskrypcji za pomocą tego menedżera:
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");
}
i mój wydawca zdarzeń :
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();
}
}
I wreszcie klasa WeakEventManager pełny kod:
/// <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;
}
}
https://msdn.microsoft.com/en-gb/library/ms404247.aspx 'Celem krótkim słabego odniesienia staje się zerowa, gdy obiekt zostanie odebrane przez odśmiecanie. "Nie sądzę, że jest coś, co uniemożliwia GC parametru delegata" TEvent " – supertopi
Również, pakowanie' Observable.FromEventPattern' do własnego operatora 'Observable.ObserveOn' jest bardzo mylące, ponieważ operator z tym samym nazwa już istnieje w klasie statycznej "Observable" (robi coś innego) – supertopi
Czy możesz dokładniej wyjaśnić, jak to rozwiązanie powinno działać? Wydaje mi się, że redagujesz jakąś logikę, która już istnieje w Rx. Jaki jest ostateczny cel używania wzorca 'WeakReference' zamiast używania zwykłego starego Rx do obsługi zdarzeń? – supertopi