Skip to content

Instantly share code, notes, and snippets.

@pmunin
Created March 26, 2017 05:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pmunin/d30c81c03b63a7f84dfba688beabe991 to your computer and use it in GitHub Desktop.
Save pmunin/d30c81c03b63a7f84dfba688beabe991 to your computer and use it in GitHub Desktop.
WeakDelegate (weak event handling)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace WeakDelegates
{
/// <summary>
/// Allows to create weak event handlers, that will unsubscribed itself when source handler object is destroyed by GC
/// </summary>
public static class WeakDelegate
{
/// <summary>
/// Create weak delegate object
/// </summary>
/// <typeparam name="TDelegate"></typeparam>
/// <param name="targetDelegate">target delegate</param>
/// <param name="generateWrapper">Should return a TDelegate wrapper that simply uses GetTarget method and if it's not null - invoke it with parameters of the delegate</param>
/// <returns></returns>
public static WeakDelegate<TDelegate> CreateWeakDelegate<TDelegate>(TDelegate targetDelegate, Func<WeakDelegate<TDelegate>, TDelegate> generateWrapper)
where TDelegate : class
{
var res = new WeakDelegate<TDelegate>(targetDelegate);
res.Delegate = generateWrapper(res);
return res;
}
public static TDelegate CreateWeakDelegate<TDelegate>(TDelegate target, Func<WeakDelegate<TDelegate>, TDelegate> generateWrapper, Action<WeakDelegate<TDelegate>> configure)
where TDelegate : class
{
var res = new WeakDelegate<TDelegate>(target);
res.Delegate = generateWrapper(res);
configure?.Invoke(res);
return res.Delegate;
}
public static WeakDelegate<Action> Create(Action action)
{
return CreateWeakDelegate(action, w => ()=>w.GetTarget()?.Invoke());
}
public static WeakDelegate<Action<T>> Create<T>(Action<T> action)
{
return CreateWeakDelegate(action, w=>(p1) => w.GetTarget()?.Invoke(p1));
}
public static WeakDelegate<Action<T1,T2>> Create<T1,T2>(Action<T1,T2> action)
{
return CreateWeakDelegate(action, wr=> (p1,p2) => wr.GetTarget()?.Invoke(p1,p2));
}
public static WeakDelegate<Action<T1, T2,T3>> Create<T1, T2,T3>(Action<T1, T2,T3> action)
{
return CreateWeakDelegate(action, wr => (p1, p2, p3) => wr.GetTarget()?.Invoke(p1, p2, p3));
}
public static WeakDelegate<Func<TRes>> Create<TRes>(Func<TRes> action)
{
return CreateWeakDelegate(action, wr => () =>
{
var target = wr.GetTarget();
if(target!=null) return target();
return default(TRes);
});
}
public static WeakDelegate<Func<T, TRes>> Create<T, TRes>(Func<T, TRes> action)
{
return CreateWeakDelegate(action, wr => (p1) =>
{
var target = wr.GetTarget();
if (target != null) return target(p1);
return default(TRes);
});
}
public static WeakDelegate<Func<T1, T2, TRes>> Create<T1, T2, TRes>(Func<T1, T2, TRes> action)
{
return CreateWeakDelegate(action, wr => (p1,p2) =>
{
var target = wr.GetTarget();
if (target != null) return target(p1,p2);
return default(TRes);
});
}
public static WeakDelegate<Func<T1, T2, T3, TRes>> Create<T1, T2, T3, TRes>(Func<T1, T2, T3, TRes> action)
{
return CreateWeakDelegate(action, wr => (p1,p2,p3) =>
{
var target = wr.GetTarget();
if (target != null) return target(p1,p2,p3);
return default(TRes);
});
}
}
public class WeakDelegate<TDelegate> where TDelegate : class
{
/// <summary>
/// Wrapped delegate that should be used instead
/// </summary>
public TDelegate Delegate;
public WeakReference TargetWeakReference { get; }
MethodInfo targetMethod = null;
public WeakDelegate(TDelegate targetDelegate)
{
var targDelegate = targetDelegate as Delegate;
if (targDelegate == null)
throw new ArgumentException("Target must be delegate");
targetMethod = targDelegate.Method;
var targetObject = targDelegate.Target;
TargetWeakReference = new WeakReference(targetObject);
}
Action ifDead;
public void IfDead(Action ifDead)
{
this.ifDead = ifDead;
}
/// <summary>
/// Returns target delegate and triggers ifDead in case reference is not alive anymore
/// </summary>
/// <returns></returns>
public TDelegate GetTarget()
{
TDelegate res = default(TDelegate);
//var isAlive = Target.TryGetTarget(out target);
var isAlive = TargetWeakReference.IsAlive;
if (!isAlive)
{
ifDead?.Invoke();
return res;
}
var targetObj = TargetWeakReference.Target;
res = System.Delegate.CreateDelegate(typeof(TDelegate), targetObj, targetMethod, true) as TDelegate;
return res;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment