|
namespace Weirdo |
|
{ |
|
using System; |
|
using System.Collections.Concurrent; |
|
using System.Linq.Expressions; |
|
using System.Reflection; |
|
|
|
/// <summary> |
|
/// Weak lambda implementation that let's you invoke Action taking parameter T |
|
/// without keeping strong reference to the instance of type T |
|
/// </summary> |
|
/// <typeparam name="T">Callback parameter to action</typeparam> |
|
public class WeakLambda<T> |
|
{ |
|
/// <summary> |
|
/// Concurrent cache dictionary to prevent runtime overhead |
|
/// </summary> |
|
private static readonly ConcurrentDictionary<MethodInfo, Action<object, T>> OpenHandleCache; |
|
|
|
/// <summary> |
|
/// The weak reference to the reference target |
|
/// </summary> |
|
private WeakReference weakTarget; |
|
|
|
/// <summary> |
|
/// Method info of passed action |
|
/// </summary> |
|
private MethodInfo methodInfo; |
|
|
|
/// <summary> |
|
/// Compiled LINQ lambda expression |
|
/// </summary> |
|
private Action<object, T> compiledExpressionMethod; |
|
|
|
/// <summary> |
|
/// Compiles given method to LINQ lambda expression invoking the method |
|
/// </summary> |
|
/// <param name="method">The method information</param> |
|
private static Action<object, T> CompileActionExpression(MethodInfo method) |
|
{ |
|
var target = Expression.Parameter(typeof(object), "target"); |
|
var e = Expression.Parameter(typeof(T), "e"); |
|
|
|
if (method.IsStatic) |
|
{ |
|
var staticCall = Expression.Call(method, e); |
|
return Expression.Lambda<Action<object, T>>(staticCall, target, e) |
|
.Compile(); |
|
} |
|
else |
|
{ |
|
var thisCastExp = Expression.Convert(target, method.DeclaringType); |
|
var boundCallExp = Expression.Call(thisCastExp, method, e); |
|
return Expression.Lambda<Action<object, T>>(boundCallExp, target, e) |
|
.Compile(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Compiles and caches expression from method info |
|
/// </summary> |
|
/// <param name="info">The method info</param> |
|
private static Action<object, T> GetExpression(MethodInfo info) |
|
{ |
|
return WeakLambda<T>.OpenHandleCache.GetOrAdd(info, WeakLambda<T>.CompileActionExpression); |
|
} |
|
|
|
/// <summary> |
|
/// Static constructor |
|
/// </summary> |
|
static WeakLambda() |
|
{ |
|
WeakLambda<T>.OpenHandleCache = new ConcurrentDictionary<MethodInfo, Action<object, T>>(); |
|
} |
|
|
|
/// <summary> |
|
/// Creates instance of WeakLambda |
|
/// </summary> |
|
/// <param name="action">The callback method to be invoked via WeakLambda</param> |
|
public WeakLambda(Action<T> action) |
|
{ |
|
// Set weak target to null in case of static methods |
|
this.weakTarget = (action.Target != null) ? new WeakReference(action.Target) : null; |
|
this.methodInfo = action.GetMethodInfo(); |
|
this.compiledExpressionMethod = WeakLambda<T>.GetExpression(this.methodInfo); |
|
} |
|
|
|
/// <summary> |
|
/// Invoke the stored action using args if object reference still exists. |
|
/// </summary> |
|
/// <param name="args">Arguments to pass to action</param> |
|
/// <returns>true if method was invoked; false otherwise</returns> |
|
public bool Invoke(T args) |
|
{ |
|
var self = this.weakTarget?.Target; |
|
|
|
// Incase of non-static self must not be null |
|
if (this.weakTarget != null && self == null) |
|
{ |
|
return false; |
|
} |
|
|
|
this.compiledExpressionMethod(self, args); |
|
return true; |
|
} |
|
} |
|
} |