C# WeakEvent

C# WeakEvent

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

    /// <summary>
    /// The forwarder-generating code is in a separate class because it does not depend on type T.
    /// </summary>
    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 WeakEvent<T> where 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<EventEntry> eventEntries;

        private object SyncRoot
        {
            get { return eventEntries; }
        }

        public WeakEvent()
        {
            eventEntries = new List<EventEntry>();
        }

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

 

posted on 2013-03-29 11:58 RockyLOMO 阅读(...) 评论(...) 编辑 收藏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值