As I promised in one of my previous posts I’m showing you corrected WeakDelegate<TDelegate> which is working with all types of delegates. The implementation was less challenging than I expected. Fortunately everything could be done with Expression Trees and usage of dynamic was not necessary.
And now it’s time for some source code:
public class WeakDelegate<TDelegate>
where TDelegate : class {
private readonly WeakReference _weakTarget;
private readonly TDelegate _proxyDelegate;
private Action<TDelegate> _cleanUp;
public WeakDelegate( TDelegate @delegate, Action<TDelegate> cleanUp ) {
var d = @delegate as Delegate;
if( d == null )
throw new ArgumentNullException( "delegate" );
if( d.Target == null || d.Method == null || d.Method.IsStatic )
throw new ArgumentException(
"Can't create weak delegate to static method." );
this._weakTarget = new WeakReference( d.Target );
this._cleanUp = cleanUp;
var targetType = d.Target.GetType();
var returnType = d.Method.ReturnType;
var lambdaArgs = d.Method.GetParameters()
.Select( ( p, i ) =>
Expression.Parameter( p.ParameterType, "a" + i ) )
.ToArray();
var targetVar = Expression.Variable( targetType, "target" );
var getTargetCall = Expression.Invoke( (Expression)
this.GetType()
.GetMethod( "GetTargetBoundExpression",
BindingFlags.Instance | BindingFlags.NonPublic )
.MakeGenericMethod( targetType )
.Invoke( this, new object[ 0 ] ) );
var returnLabel = Expression.Label( returnType );
var defaultReturn = Expression.Default( returnType );
var lambda = Expression.Lambda<TDelegate>( Expression.Block(
new[] { targetVar },
new Expression[] {
Expression.Assign( targetVar, getTargetCall ),
Expression.IfThenElse(
Expression.Equal( targetVar, Expression.Constant( null ) ),
Expression.Return( returnLabel, defaultReturn ),
Expression.Return( returnLabel,
Expression.Call( targetVar, d.Method, lambdaArgs ) ) ),
Expression.Label( returnLabel, defaultReturn )
} ), lambdaArgs );
this._proxyDelegate = lambda.Compile();
}
public static implicit operator TDelegate(
WeakDelegate<TDelegate> weakDelegate ) {
return weakDelegate != null ? weakDelegate._proxyDelegate : null;
}
private Expression GetTargetBoundExpression<TTarget>() {
return (Expression<Func<TTarget>>) ( () => this.GetTarget<TTarget>() );
}
private TTarget GetTarget<TTarget>() {
var target = (TTarget) this._weakTarget.Target;
if( target != null )
return target;
if( this._cleanUp != null ) {
this._cleanUp( this._proxyDelegate );
this._cleanUp = null;
}
return target;
}
}
In lines from 23 to 50 using types read from original delegate and expression trees I’m creating proxy TDelegate simmilar to the following metacode:
this._proxyDelegate = (a0...aN) => {
var target = this.GetTarget<TTarget>();
if( target == null )
return default( TDelegateReturnType );
else
return target.OriginalDelegateMethod( a0...aN );
}
The implementation is sill quite fast – only 6-9 times slower than direct delegate invocation. It has a lower performance than WeakEventHandler<TEventHandler> described in one of my previous posts which is only 4-6 times slower.
There are also few drawbacks of my solution:
- after the target is garbage collected, the proxy delegate will return default value of the
TDelegate‘s return type (I think that this can be solved somehow using for examplecleanUpcallback) - cleaning up is not thread safe etc.
The source code with examples is here.


















