Here's my first attempt at creating a WeakAction using Expressions and Delegates. I would like it to be clear that this code was written after reading serveral blogs and taking snippets from various sources.
The Program class at the bottom shows a few examples of how to use it.
Does anyone have any performance improvements when it comes to creating delegates? I'm still trying to get my head around them. All suggestions and any constructive feedback is welcome.
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace WeakAction
{
public class WeakAction<T> where T : class
{
private readonly WeakReference weakRef;
private readonly Type ownerType;
private readonly string methodName;
public WeakAction(object instance, Expression expression)
{
var method = GetMethodInfo(expression);
methodName = method.Name;
if (instance == null)
{
if (!method.IsStatic)
{
throw new ArgumentException("instance cannot be null for instance methods");
}
this.ownerType = method.DeclaringType;
}
else
{
this.weakRef = new WeakReference(instance);
}
}
public T Delegate
{
get { return this.GetMethod(); }
}
public bool HasBeenCollected
{
get
{
return this.ownerType == null &&
(this.weakRef == null || !this.weakRef.IsAlive);
}
}
public bool IsInvokable
{
get { return !HasBeenCollected; }
}
private static MethodInfo GetMethodInfo(Expression expression)
{
LambdaExpression lambda = expression as LambdaExpression;
if (lambda == null)
{
throw new ArgumentException("expression is not LambdaExpression");
}
MethodCallExpression outermostExpression = lambda.Body as MethodCallExpression;
if (outermostExpression == null)
{
throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
}
return outermostExpression.Method;
}
private T GetMethod()
{
if (this.ownerType != null)
{
return System.Delegate.CreateDelegate(typeof(T), this.ownerType, this.methodName) as T;
}
if (this.weakRef != null && this.weakRef.IsAlive)
{
object localTarget = this.weakRef.Target;
if (localTarget != null)
{
return System.Delegate.CreateDelegate(typeof(T), localTarget, this.methodName) as T;
}
}
return null;
}
}
}
using System;
using System.Linq.Expressions;
namespace WeakAction
{
public class Program
{
public static void Main()
{
var testClass = new TestClass();
// instance method
Expression<Func<int>> instanceExpression = () => testClass.Stuff();
var weakStuff = new WeakAction<Func<int>>(testClass, instanceExpression);
if (!weakStuff.HasBeenCollected)
{
var result = weakStuff.Delegate();
}
// static method
Expression<Func<int>> staticExpression = () => TestClass.MoreStuff();
var staticWeakRef = new WeakAction<Func<int>>(null, staticExpression);
if (!staticWeakRef.HasBeenCollected)
{
var result = staticWeakRef.Delegate();
}
// instance method taking an arg
Expression<Action<string>> argExpression = (str) => testClass.TakesAnArg(str);
var instanceWithArg = new WeakAction<Action<string>>(testClass, argExpression);
if (instanceWithArg.IsInvokable)
{
instanceWithArg.Delegate("hello bert!");
}
Console.Read();
}
public class TestClass
{
public int Stuff()
{
Console.WriteLine("Stuff invoked!");
return 1;
}
public void TakesAnArg(string msg)
{
Console.WriteLine(msg);
}
public static int MoreStuff()
{
Console.WriteLine("More stuff invoked!");
return 1;
}
}
}
}