/// <summary>
        /// Publish the specified change record to all <see cref="Subscribers"/>.
        /// </summary>
        /// <param name="changeRecord">The change record to publish.</param>
        /// <exception cref="ArgumentNullException"><paramref name="changeRecord"/> is <see langword="null" />.</exception>
        public void Publish(ChangeRecord changeRecord)
        {
            if (changeRecord == null)
            {
                throw new ArgumentNullException(nameof(changeRecord));
            }

            // local snapshot of subscribers
            ISubscription[] subscribers;
            lock (_subscribers)
                subscribers = _subscribers.ToArray();

            // notify all subscribers that are active and match filter
            var deadSubscribers = subscribers
                                  .Where(h => IsMatch(changeRecord, h.Filter))
                                  .Where(h => !h.BeginInvoke(changeRecord))
                                  .ToList();

            if (deadSubscribers.Count == 0)
            {
                return;
            }

            // remove dead subscribers
            lock (_subscribers)
                foreach (var d in deadSubscribers)
                {
                    _subscribers.Remove(d);
                }
        }
        private bool IsMatch(ChangeRecord changeRecord, string filter)
        {
            if (string.IsNullOrWhiteSpace(filter))
            {
                return(true);
            }

            return(changeRecord.Namespace.Like(filter));
        }
        /// <summary>
        /// Begin invoke of <see cref="Handler"/> on the thread-pool background thread.
        /// </summary>
        /// <param name="change">The change record to send.</param>
        /// <returns><c>true</c> if the Handler is still alive and was able to be invoked; otherwise <c>false</c>.</returns>
        public bool BeginInvoke(ChangeRecord change)
        {
            // handler might have been disposed
            var handler = _reference.Target as IHandleChange;

            if (handler == null)
            {
                return(false);
            }

            // fire and forget
            return(ThreadPool.QueueUserWorkItem(state => handler.HandleChange(change)));
        }