public static ReadOnlyDictionarySubject <TKey, TValue> Collect <TKey, TValue>(this IObservable <CollectionModification <KeyValuePair <TKey, TValue> > > source)
        {
            Contract.Requires(source != null);
            Contract.Ensures(Contract.Result <ReadOnlyDictionarySubject <TKey, TValue> >() != null);

            var subscription = new SingleAssignmentDisposable();

            var dictionary = new DictionarySubject <TKey, TValue>(subscription);

            subscription.Disposable = source.Subscribe(dictionary);

            return(new ReadOnlyDictionarySubject <TKey, TValue>(dictionary));
        }
        /// <summary>
        /// Adds the elements from the specified observable sequence into a <see cref="ReadOnlyDictionarySubject{TKey,TValue}"/>.
        /// </summary>
        /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
        /// <param name="source">The sequence from which elements are collected.</param>
        /// <param name="keySelector">A function that maps values to keys.</param>
        /// <returns>A <see cref="ReadOnlyDictionarySubject{TKey,TValue}"/> that receives the elements from the specified sequence.</returns>
        public static ReadOnlyDictionarySubject <TKey, TValue> Collect <TKey, TValue>(this IObservable <TValue> source, Func <TValue, TKey> keySelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(keySelector != null);
            Contract.Ensures(Contract.Result <ReadOnlyDictionarySubject <TKey, TValue> >() != null);

            var subscription = new SingleAssignmentDisposable();

            var dictionary = new DictionarySubject <TKey, TValue>(subscription);

            subscription.Disposable = source.Subscribe(value => dictionary[keySelector(value)] = value, dictionary.OnError, dictionary.OnCompleted);

            return(new ReadOnlyDictionarySubject <TKey, TValue>(dictionary));
        }
        protected override void Main()
        {
            var source = new DictionarySubject <string, CustomValue>();

            var monitorKeys = new[] { "A", "C", "D", "E", "F", "G" };

            // PropertyChanges doesn't work on KeyValuePair because it doesn't have any property change events.
            // So we must strip off the key and create a sequence of collection notifications for the values only.
            using (source
                   .Where(
                       exists: _ => false,
                       onAdded: added => monitorKeys.Contains(added.Key),
                       onReplaced: (replaced, added) => monitorKeys.Contains(replaced.Key) && monitorKeys.Contains(added.Key),
                       onRemoved: removed => monitorKeys.Contains(removed.Key),
                       onCleared: () => false)
                   .Select(
                       _ => null,
                       added => CollectionNotification.CreateOnAdded(added.Value),
                       (replaced, added) => CollectionNotification.CreateOnReplaced(replaced.Value, added.Value),
                       removed => CollectionNotification.CreateOnRemoved(removed.Value),
                       () => null)
                   .PropertyChanges()
                   .Select(e => new { Object = (CustomValue)e.Sender, Property = e.EventArgs.PropertyName })
                   .Subscribe(value => TraceLine("Changed {0}: {1}", value.Property, value.Object)))
            {
                var a = new CustomValue("A");
                var b = new CustomValue("B");
                var c = new CustomValue("C");

                source.Add(a.Name, a);
                source.Add(b.Name, b);
                source.Add(c.Name, c);

                a.Child = new CustomValue("D");
                b.Child = new CustomValue("E");
                c.Child = new CustomValue("F");

                a.Child.Child = new CustomValue("G");

                a.Number = 1;
                b.Number = 1;
                c.Number = 1;

                a.Child.Number = 100;
                b.Child.Number = 101;
                c.Child.Number = 102;

                var child      = a.Child;
                var grandchild = child.Child;

                grandchild.Number = 103;

                child.Child = null;

                grandchild.Number = 104;

                child.Number = 105;

                a.Child = null;

                child.Number = 106;

                source.Remove(a.Name);

                a.Number = 2;
                b.Number = 2;
                c.Number = 2;

                child.Number   = 200;
                b.Child.Number = 201;
                c.Child.Number = 202;

                grandchild.Number = 203;

                source.Remove(b.Name);
                source.Remove(c.Name);

                a.Number = 4;
                b.Number = 4;
                c.Number = 4;

                child.Number   = 300;
                b.Child.Number = 301;
                c.Child.Number = 302;

                grandchild.Number = 303;
            }
        }