Ejemplo n.º 1
0
        /// <summary>
        /// 订阅弱事件处理函数。
        /// </summary>
        /// <param name="originalHandler">原始处理函数,请始终传入 <code>value</code>。</param>
        /// <param name="castedHandler">可被隐式转换为 Action 的方法组,请始终传入 <code>value.Invoke</code>。</param>
        public void Add(MulticastDelegate originalHandler, Action <TSender, TArgs> castedHandler)
        {
            lock (_locker)
            {
                // 获取委托对应的目标实例。
                var target = originalHandler.Target ?? throw new ArgumentException("无法将弱事件应用于没有对象的纯函数中。", nameof(originalHandler));
                var method = originalHandler.Method;

                // 找到目前是否有已经存储过的对 target 的弱引用实例,如果有,我们将复用此实例,而不是加入到集合中。
                // 注意,这里的判定使用的是 ReferenceEquals,因为 ConditionalWeakTable 的比较用的是此方法,这可以确保回收时机两者一致。
                var reference = _relatedInstances.Find(x => x.TryGetTarget(out var instance) && ReferenceEquals(target, instance));
                if (reference is null)
                {
                    // 如果没有找到已经存储过的弱引用实例,我们将创建一个新的。
                    reference = new WeakReference <object>(target);
                    _relatedInstances.Add(reference);
                    var weakEventHandler = new WeakEventHandler();
                    weakEventHandler.Add(originalHandler, castedHandler);
                    _handlers.Add(target, weakEventHandler);
                }
                else if (_handlers.TryGetValue(target, out var weakEventHandler))
                {
                    // 如果找到了已经存储过的弱引用实例,则为其添加一个新的事件处理器。
                    weakEventHandler.Add(originalHandler, castedHandler);
                }
                else
                {
                    // 如果找不到弱引用实例,说明有一个已经被 GC 掉的对象竟然还能 += 事件。逗我?!
                    throw new InvalidOperationException("有一个已经被 GC 掉的对象正在试图注册事件处理函数,可能代码写错了。");
                }
            }
        }