public static IDisposable connect_item_hooks <T>(IReadOnlyObservableCollection <T> source, Func <T, IDisposable> get_item_hook)
        {
            var item_hooks = source.Select(get_item_hook).ToList();

            return(new CompositeDisposable(
                       Disposable.wrap_collection(item_hooks),
                       source.Subscribe((s, e) => CollectionHelper.reflect_change(item_hooks, source, get_item_hook, d => d.Dispose(), e))));
        }
 /// <summary>
 ///     Initializes a new instance of the <see cref="TransformingReadOnlyObservableList{TSource, TTarget}" /> class.
 /// </summary>
 /// <param name="source">The source collection.</param>
 /// <param name="selector">The transformation to apply to the items in <paramref name="source" />.</param>
 /// <param name="onRemove">The callback to execute when an item is removed from the collection.</param>
 /// <param name="targetComparer">
 /// A comparer for the list items. This is only used if the source collection is not a list
 /// and does not provide index information in its <see cref="NotifyCollectionChangedEventArgs"/> events.
 /// </param>
 public TransformingReadOnlyObservableList(IReadOnlyObservableCollection <TSource> source,
                                           Func <TSource, TTarget> selector, Action <TTarget> onRemove = null, IEqualityComparer <TTarget> targetComparer = null)
     : base(new ObservableCollection <TTarget>(source.Select(selector)))
 {
     _source         = source;
     _selector       = selector;
     _onRemove       = onRemove;
     _sourceComparer = targetComparer ?? EqualityComparer <TTarget> .Default;
     CollectionChangedEventManager.AddListener(source, this);
 }