using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Developpez.Dotnet.Reflection;
#if SILVERLIGHT
using Developpez.Dotnet.Collections;
#endif
namespace Developpez.Dotnet
{
///
/// Implémente un évènement qui ne garde qu'une référence faible vers ses handlers,
/// de façon à éviter les fuites mémoire.
///
/// Type du delegate de l'évènement
public class WeakEvent
{
// ReSharper disable StaticFieldInGenericType
private static readonly MethodInfo _invokeMethod;
// ReSharper restore StaticFieldInGenericType
private static readonly Func, TEventHandler> _raiseDelegateGenerator;
private readonly TEventHandler _raiseDelegate;
private readonly List> _handlers;
static WeakEvent()
{
if (!typeof(TEventHandler).Is())
throw new ArgumentException("The type parameter must be a delegate type");
#if NETFX_CORE
_invokeMethod = typeof (TEventHandler).GetTypeInfo().GetDeclaredMethod("Invoke");
#else
_invokeMethod = typeof(TEventHandler).GetMethod("Invoke");
#endif
var invokeParameters =
_invokeMethod.GetParameters()
.Select(p => Expression.Parameter(p.ParameterType, p.Name))
.ToArray();
var raiseParams =
Expression.NewArrayInit(
typeof(object),
invokeParameters.AsEnumerable());
#if NETFX_CORE
var raiseMethod = typeof (WeakDelegate).GetTypeInfo().GetDeclaredMethod("RaiseInternal");
#else
var raiseMethod = typeof(WeakEvent).GetMethod("RaiseInternal", BindingFlags.NonPublic | BindingFlags.Instance);
#endif
var tWeakEvent = typeof(WeakEvent);
var weakEventParam = Expression.Parameter(tWeakEvent, "weakEvent");
// ReSharper disable PossiblyMistakenUseOfParamsMethod
Expression callExpr =
Expression.Call(
weakEventParam,
raiseMethod,
raiseParams);
// ReSharper restore PossiblyMistakenUseOfParamsMethod
if (_invokeMethod.ReturnType != typeof(void))
{
callExpr = Expression.Convert(callExpr, _invokeMethod.ReturnType);
}
var raiseExpr =
Expression.Lambda(
callExpr,
invokeParameters);
var generatorExpr =
Expression.Lambda, TEventHandler>>(
raiseExpr,
weakEventParam);
_raiseDelegateGenerator = generatorExpr.Compile();
}
///
/// Initialise une nouvelle instance de
///
public WeakEvent()
{
_raiseDelegate = _raiseDelegateGenerator(this);
_handlers = new List>();
}
///
/// Ajoute un handler à l'évènement
///
/// Le handler à ajouter
public virtual void AddHandler(TEventHandler handler)
{
Delegate d = (Delegate)(object)handler;
_handlers.Add(new WeakDelegate(d));
}
///
/// Retire un handler de l'évènement
///
/// Le handler à retirer
public virtual void RemoveHandler(TEventHandler handler)
{
// also remove "dead" (garbage collected) handlers
_handlers.RemoveAll(wd => !wd.IsAlive || wd.Equals(handler));
}
///
/// Renvoie un delegate qui déclenche l'évènement en appelant tous les handlers encore actifs
///
public TEventHandler Raise
{
get { return _raiseDelegate; }
}
internal object RaiseInternal(params object[] args)
{
var handlers = _handlers.ToArray();
object result = _invokeMethod.ReturnType.GetDefaultValue();
foreach (var weakDelegate in handlers)
{
result = weakDelegate.Invoke(args);
if (!weakDelegate.IsAlive)
_handlers.Remove(weakDelegate);
}
return result;
}
}
}