view Implab/Components/ComponentContainer.cs @ 238:bdfdba6b645b v2

fixed unpredictable Safe.Dispose behaviour
author cin
date Fri, 01 Dec 2017 01:28:56 +0300
parents 1e082fb67a46
children
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.Linq;

namespace Implab.Components {
    /// <summary>
    /// Component container, used to store track components in multi-threaded environmment.
    /// </summary>
    /// <remarks>Instanses of this class are thread safe.</remarks>
    public class ComponentContainer<T> : Disposable, ICollection<T> {
        List<T> m_components = new List<T>();
        readonly object m_lock = new object();

        /// <summary>
        /// Removes currently stored compoenents from the container and disposes them if possible.
        /// </summary>
        /// <remarks>
        /// A new components may be added before this method completes.
        /// </remarks>
        public void Clear() {
            List<T> removed;

            lock (m_lock) {
                removed = m_components;
                m_components = new List<T>();
            }

            foreach (var item in removed.OfType<IDisposable>())
                item.Dispose();
        }

        /// <summary>
        /// Checks whether the specified item in the collection.
        /// </summary>
        /// <param name="item">The item to check.</param>
        public bool Contains(T item) {
            lock (m_lock)
                return m_components.Contains(item);
        }

        /// <summary>
        /// Copies currently stored components to the specified array.
        /// </summary>
        /// <param name="array">A destination array for components.</param>
        /// <param name="arrayIndex">A starting index in the destination array.</param>
        public void CopyTo(T[] array, int arrayIndex) {
            lock (m_lock)
                m_components.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Remove the specified item from the collection.
        /// </summary>
        /// <param name="item">The item to remove.</param>
        public bool Remove(T item) {
            lock (m_lock)
                return m_components.Remove(item);
        }

        /// <summary>
        /// Gets the count of components in the collection.
        /// </summary>
        public int Count {
            get {
                lock (m_lock)
                    return m_components.Count;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is read only.
        /// </summary>
        /// <remarks>
        /// Always false.
        /// </remarks>
        public bool IsReadOnly {
            get {
                return false;
            }
        }

        /// <summary>
        /// Gets the enumerator for components in the collection.
        /// </summary>
        /// <returns>The enumerator.</returns>
        public IEnumerator<T> GetEnumerator() {
            T[] items = new T[m_components.Count];
            lock (m_lock) {
                m_components.CopyTo(items);
            }
            return (IEnumerator<T>)items.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        /// <summary>
        /// Add the specified item to the collection.
        /// </summary>
        /// <param name="item">The item to add.</param>
        /// <remarks>
        /// If the collection is alredy disposed, the item isn't added to the collection and disposed if possible.
        /// </remarks>
        public void Add(T item) {
            Safe.ArgumentNotNull(item, "item");
            bool dispose = false;
            lock (m_lock) {
                if (IsDisposed)
                    dispose = true;
                else
                    m_components.Add(item);
            }
            if (dispose)
                Safe.Dispose(item);
        }

        /// <summary>
        /// Disposes the components stored in the collection.
        /// </summary>
        /// <param name="disposing">If set to <c>true</c> the collection is disposing.</param>
        protected override void Dispose(bool disposing) {
            if (disposing)
                Clear();

            base.Dispose(disposing);
        }
    }
}