Skip to content

Instantly share code, notes, and snippets.

@aloisdeniel
Created January 10, 2017 01:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aloisdeniel/9689a7ffb403bd85e206487fbb8bd970 to your computer and use it in GitHub Desktop.
Save aloisdeniel/9689a7ffb403bd85e206487fbb8bd970 to your computer and use it in GitHub Desktop.
Weak event handlers to avoid keeping strong reference to subscriber of an event.
namespace Utils
{
using System;
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// Weak event handlers to avoid keeping strong reference to subscriber of an event.
/// </summary>
public static class WeakEventHandlers
{
public static List<IWeakEventHandler> subscriptions = new List<IWeakEventHandler>();
public static DateTime LastPurge { get; private set; } = DateTime.Now;
public static TimeSpan PurgeIntervalle = TimeSpan.FromSeconds(10);
/// <summary>
/// Adds the weak event handler.
/// </summary>
/// <param name="source">Source.</param>
/// <param name="eventName">Event name.</param>
/// <param name="handler">Handler.</param>
/// <typeparam name="TEventArgs">The 1st type parameter.</typeparam>
public static void AddWeakHandler<TEventArgs>(this object source, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
{
var whandler = new WeakEventHandler<TEventArgs>(source, eventName, handler);
subscriptions.Add(whandler);
if (DateTime.Now - LastPurge >= PurgeIntervalle)
{
Purge();
}
}
/// <summary>
/// Purge all event subscriptions which target has been garbage collected.
/// </summary>
public static void Purge()
{
for (int i = 0; i < subscriptions.Count; )
{
var subscription = subscriptions[i];
if (subscription.CheckUnsubscribe())
{
subscriptions.Remove(subscription);
}
else
{
i++;
}
}
LastPurge = DateTime.Now;
}
}
public interface IWeakEventHandler
{
bool CheckUnsubscribe();
}
/// <summary>
/// A light event handler, only this object will be kept in memory (and will keep a reference to source).
/// This could be considered as acceptable.
/// </summary>
public class WeakEventHandler<TEventArgs> : IWeakEventHandler where TEventArgs : EventArgs
{
public WeakEventHandler(object source, string eventName, EventHandler<TEventArgs> target)
{
this.eventInfo = source.GetType().GetEvent(eventName);
this.targetReference = new WeakReference(target.Target);
this.targetMethod = target.Method;
var methodInfo = this.GetType().GetMethod(nameof(OnEvent), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
eventHandler = Delegate.CreateDelegate(this.eventInfo.EventHandlerType, this, methodInfo);
this.eventInfo.AddEventHandler(source, eventHandler);
}
#region Fields
private Delegate eventHandler;
private readonly EventInfo eventInfo;
private readonly WeakReference targetReference;
private readonly MethodInfo targetMethod;
#endregion
private void OnEvent(object sender, TEventArgs args)
{
if (this.targetReference.IsAlive)
{
this.targetMethod.Invoke(this.targetReference.Target, new object[] { sender, args });
}
}
public bool CheckUnsubscribe()
{
if (!targetReference.IsAlive)
{
this.@event.RemoveEventHandler(this,eventHandler);
return true;
}
return false;
}
}
}
viewmodel.AddWeakHandler<PropertyChangedEventArgs>(nameof(INotifyPropertyChanged.PropertyChanged), this.OnViewModelPropertyChanged);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment