Created
March 26, 2017 05:00
-
-
Save pmunin/d30c81c03b63a7f84dfba688beabe991 to your computer and use it in GitHub Desktop.
WeakDelegate (weak event handling)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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