Last active
November 12, 2015 23:26
-
-
Save Torvin/157b2a3e2f34d3491c8c to your computer and use it in GitHub Desktop.
WeakDelegeate
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.Linq; | |
using System.Linq.Expressions; | |
public class WeakDelegate<T> | |
where T : class | |
{ | |
private static readonly Func<WeakDelegate<T>, T> InvokerFactory = CreateInvokerFactory(); | |
/// <summary> | |
/// Creates and returns a delegate in this form: (WeakDelegate{T} wd) => ((arg1, arg2,..) => wd.DoInvoke(new object[] { arg1, arg2,.. })). | |
/// We can afterwards pass a reference to WeakDelegate and obtain a working invoker | |
/// </summary> | |
/// <remarks> | |
/// This method is called only once for each T. | |
/// </remarks> | |
private static Func<WeakDelegate<T>, T> CreateInvokerFactory() | |
{ | |
var type = typeof(T); | |
if (!typeof(Delegate).IsAssignableFrom(type)) | |
throw new ArgumentException("T should be a delegate returning void"); | |
var wd = Expression.Parameter(typeof(WeakDelegate<T>)); | |
// `invoker` is a lambda in form: (arg1, arg2,..) => wd.DoInvoke(new object[] { arg1, arg2,.. }) | |
var method = type.GetMethod("Invoke"); // Delegate.Invoke() has parameters types matching delegate parameters types | |
var pars = method.GetParameters().Select(pi => Expression.Parameter(pi.ParameterType)).ToArray(); | |
var invoker = | |
Expression.Lambda<T>( | |
Expression.Call(wd, GetMethod(x => x.DoInvoke(null)), Expression.NewArrayInit(typeof(object), pars.Select(p => Expression.Convert(p, typeof(object))))), | |
pars | |
); | |
return (Func<WeakDelegate<T>, T>)Expression.Lambda(invoker, new[] { wd }).Compile(); | |
} | |
// clever way to get MethodInfo without Type.GetMethod() | |
private static System.Reflection.MethodInfo GetMethod(Expression<Action<WeakDelegate<T>>> action) | |
{ | |
return ((MethodCallExpression)action.Body).Method; | |
} | |
private readonly T _invoker; | |
private readonly List<WeakReference<T>> _actions = new List<WeakReference<T>>(); | |
public WeakDelegate() | |
{ | |
_invoker = InvokerFactory(this); | |
} | |
public void Add(T action) | |
{ | |
lock (_actions) | |
_actions.Add(new WeakReference<T>(action)); | |
} | |
private void DoInvoke(object[] args) | |
{ | |
lock (_actions) | |
{ | |
var dead = new List<WeakReference<T>>(); | |
foreach (var reference in _actions) | |
{ | |
T action; | |
if (reference.TryGetTarget(out action)) | |
{ | |
((Delegate)(object)action).DynamicInvoke(args); | |
} | |
else | |
{ | |
dead.Add(reference); | |
} | |
} | |
foreach (var reference in dead) | |
_actions.Remove(reference); | |
} | |
} | |
/// <summary> | |
/// Looks like a property, but acts like a method! | |
/// </summary> | |
public T Invoke | |
{ | |
get { return _invoker; } | |
} | |
} | |
class Demo | |
{ | |
public static void Main() | |
{ | |
var wd1 = new WeakDelegate<Action<int>>(); | |
var wd2 = new WeakDelegate<EventHandler>(); | |
wd1.Add(x => Console.WriteLine("Action<int> called with: " + x)); | |
wd2.Add((sender, args) => Console.WriteLine("EventHandler called")); | |
// type-safe invoke | |
wd1.Invoke(123); | |
wd2.Invoke(null, EventArgs.Empty); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment