using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Reflection.Emit;using System.Runtime.CompilerServices;using System.Text;namespace Rocky{ internal delegate bool Forwarder(WeakReference weakRef, object sender, EventArgs e); ////// The forwarder-generating code is in a separate class because it does not depend on type T. /// internal static class WeakEventForwarderProvider { private static MethodInfo getTarget; private static Type[] forwarderParameters; private static Hashtable forwarders; static WeakEventForwarderProvider() { getTarget = typeof(WeakReference).GetMethod("get_Target"); forwarderParameters = new Type[] { typeof(WeakReference), typeof(object), typeof(EventArgs) }; forwarders = Hashtable.Synchronized(new Hashtable()); } internal static Forwarder GetForwarder(MethodInfo method) { var fd = (Forwarder)forwarders[method]; if (fd != null) { return fd; } if (method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) { throw new ArgumentException("Cannot create weak event to anonymous method with closure."); } ParameterInfo[] parameters = method.GetParameters(); DynamicMethod dm = new DynamicMethod("WeakEvent", typeof(bool), forwarderParameters, method.DeclaringType); ILGenerator il = dm.GetILGenerator(); if (!method.IsStatic) { il.Emit(OpCodes.Ldarg_0); il.EmitCall(OpCodes.Callvirt, getTarget, null); il.Emit(OpCodes.Dup); Label label = il.DefineLabel(); il.Emit(OpCodes.Brtrue, label); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Ret); il.MarkLabel(label); // The castclass here is required for the generated code to be verifiable. // We can leave it out because we know this cast will always succeed // (the instance/method pair was taken from a delegate). // Unverifiable code is fine because private reflection is only allowed under FullTrust // anyways. //il.Emit(OpCodes.Castclass, method.DeclaringType); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); // This castclass here is required to prevent creating a hole in the .NET type system. // See Program.TypeSafetyProblem in the 'SmartWeakEventBenchmark' to see the effect when // this cast is not used. // You can remove this cast if you trust add FastSmartWeakEvent.Raise callers to do // the right thing, but the small performance increase (about 5%) usually isn't worth the risk. il.Emit(OpCodes.Castclass, parameters[1].ParameterType); il.EmitCall(OpCodes.Call, method, null); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ret); forwarders[method] = fd = (Forwarder)dm.CreateDelegate(typeof(Forwarder)); return fd; } internal static void OverForwarder(MethodInfo method) { forwarders.Remove(method); } }}
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Collections.Specialized;using System.Reflection;using System.Reflection.Emit;namespace Rocky{ public sealed class WeakEventwhere T : class { #region NestedTypes private struct EventEntry { public readonly Forwarder Forwarder; public readonly MethodInfo TargetMethod; public readonly WeakReference TargetReference; public EventEntry(Forwarder forwarder, MethodInfo targetMethod, WeakReference targetReference) { this.Forwarder = forwarder; this.TargetMethod = targetMethod; this.TargetReference = targetReference; } } #endregion #region Static static WeakEvent() { Type type = typeof(T); if (!type.IsSubclassOf(typeof(Delegate))) { throw new ArgumentException("T must be a delegate type"); } MethodInfo invoke = type.GetMethod("Invoke"); if (invoke == null || invoke.GetParameters().Length != 2) { throw new ArgumentException("T must be a delegate type taking 2 parameters"); } ParameterInfo senderParameter = invoke.GetParameters()[0]; if (senderParameter.ParameterType != typeof(object)) { throw new ArgumentException("The first delegate parameter must be of type 'object'"); } ParameterInfo argsParameter = invoke.GetParameters()[1]; if (!(typeof(EventArgs).IsAssignableFrom(argsParameter.ParameterType))) { throw new ArgumentException("The second delegate parameter must be derived from type 'EventArgs'"); } if (invoke.ReturnType != typeof(void)) { throw new ArgumentException("The delegate return type must be void."); } } #endregion private List eventEntries; private object SyncRoot { get { return eventEntries; } } public WeakEvent() { eventEntries = new List (); } private void RemoveEntry(int index, MethodInfo method) { lock (SyncRoot) { eventEntries.RemoveAt(index); } WeakEventForwarderProvider.OverForwarder(method); } private void RemoveDeadEntries() { lock (SyncRoot) { eventEntries.RemoveAll(entry => { if (entry.TargetReference != null && !entry.TargetReference.IsAlive) { WeakEventForwarderProvider.OverForwarder(entry.TargetMethod); return true; } return false; }); } } public void Add(T e) { if (e == null) { return; } Delegate d = (Delegate)(object)e; if (eventEntries.Count == eventEntries.Capacity) { RemoveDeadEntries(); } MethodInfo targetMethod = d.Method; object targetInstance = d.Target; WeakReference target = targetInstance != null ? new WeakReference(targetInstance) : null; lock (SyncRoot) { eventEntries.Add(new EventEntry(WeakEventForwarderProvider.GetForwarder(targetMethod), targetMethod, target)); } } public void Remove(T e) { if (e == null) { return; } Delegate d = (Delegate)(object)e; object targetInstance = d.Target; MethodInfo targetMethod = d.Method; lock (SyncRoot) { for (int i = eventEntries.Count - 1; i >= 0; i--) { EventEntry entry = eventEntries[i]; if (entry.TargetReference != null) { object target = entry.TargetReference.Target; if (target == null) { RemoveEntry(i, entry.TargetMethod); } else if (target == targetInstance && entry.TargetMethod == targetMethod) { RemoveEntry(i, entry.TargetMethod); break; } } else { if (targetInstance == null && entry.TargetMethod == targetMethod) { RemoveEntry(i, entry.TargetMethod); break; } } } } } public bool Raise() { return Raise(this, EventArgs.Empty); } public bool Raise(object sender, EventArgs e) { bool needClear = false; foreach (EventEntry entry in eventEntries) { needClear |= entry.Forwarder(entry.TargetReference, sender, e); } if (needClear) { RemoveDeadEntries(); } return eventEntries.Count > 0; } }}