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; } } }