Skip to main content

Provides a generic read-only (immutable) Dictionary class.

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

namespace LinqToLdap.Collections
{
    /// <summary>
    /// A read only generic dictionary.  Taken from http://www.blackwasp.co.uk/ReadOnlyDictionary.aspx
    /// </summary>
    public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private readonly IDictionary<TKey, TValue> _dictionary;
        private ICollection<TKey> _keys;
        private ICollection<TValue> _values;

        ///<summary>
        /// Creates an empty read only dictionary
        ///</summary>
        public ReadOnlyDictionary()
        {
            _dictionary = new Dictionary<TKey, TValue>();
        }

        /// <summary>
        /// Creates a read only dictionary from an existing enumerable <see cref="KeyValuePair{TKey,TValue}"/>.
        /// </summary>
        /// <param name="source"></param>
        public ReadOnlyDictionary(IEnumerable<KeyValuePair<TKey, TValue>> source) : this(source, null)
        {
        }

        /// <summary>
        /// Creates a read only dictionary from an existing enumerable <see cref="KeyValuePair{TKey,TValue}"/>.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="comparer"></param>
        public ReadOnlyDictionary(IEnumerable<KeyValuePair<TKey, TValue>> source, IEqualityComparer<TKey> comparer)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));

            _dictionary = comparer == null
                ? source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
                : source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, comparer);
        }

        /// <summary>
        /// Creates a read only dictionary from an existing enumerable <see cref="KeyValuePair{TKey,TValue}"/>.
        /// </summary>
        /// <param name="source"></param>
        public ReadOnlyDictionary(Dictionary<TKey, TValue> source)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));

            _dictionary = new Dictionary<TKey, TValue>(source, source.Comparer);
        }

        #region IDictionary<TKey,TValue> Members

        void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Determines whether the <see cref="ReadOnlyDictionary{TKey,TValue}"/> contains the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the <see cref="ReadOnlyDictionary{TKey,TValue}"/>.</param>
        /// <returns></returns>
        public bool ContainsKey(TKey key)
        {
            return _dictionary.ContainsKey(key);
        }

        /// <summary>
        /// Gets a collection containing the keys in the <see cref="ReadOnlyDictionary{TKey,TValue}"/>.
        /// </summary>
        public ICollection<TKey> Keys
        {
            get { return _keys ?? (_keys = new ReadOnlyCollection<TKey>(new List<TKey>(_dictionary.Keys))); }
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">
        /// When this method returns, contains the value associated with the specified key,
        /// if the key is found; otherwise, the default value for the type of the value parameter.
        /// This parameter is passed uninitialized.
        /// </param>
        /// <returns></returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        /// <summary>
        /// Gets a collection containing the values in the <see cref="ReadOnlyDictionary{TKey,TValue}"/>.
        /// </summary>
        public ICollection<TValue> Values
        {
            get { return _values ?? (_values = new ReadOnlyCollection<TValue>(new List<TValue>(_dictionary.Values))); }
        }

        ///<summary>
        /// Gets or sets the value associated with the specified key.
        ///</summary>
        ///<param name="key">The key of the value to get or set.</param>
        public TValue this[TKey key]
        {
            get
            {
                return _dictionary[key];
            }
        }

        TValue IDictionary<TKey, TValue>.this[TKey key]
        {
            get
            {
                return this[key];
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        #endregion IDictionary<TKey,TValue> Members

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Dictionary is read only
        /// </summary>
        /// <exception cref="NotSupportedException">
        /// Thrown because the dictionary is read only
        /// </exception>
        public void Clear()
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Determines whether the <see cref="IDictionary{TKey,TValue}"/> contains an element with the specified key.
        /// </summary>
        /// <param name="item">The key to locate in the <see cref="IDictionary{TKey,TValue}"/>.</param>
        /// <returns></returns>
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _dictionary.Contains(item);
        }

        /// <summary>
        /// Copies the elements of the <see cref="IDictionary{TKey,TValue}"/> to an array,
        /// starting at the specified array index.
        /// </summary>
        /// <param name="array">
        /// The one-dimensional array that is the destination of the elements
        /// copied from <see cref="IDictionary{TKey,TValue}"/>. The array must have zero-based indexing.
        /// </param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _dictionary.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of elements in the dictionary.
        /// </summary>
        public int Count
        {
            get
            {
                return _dictionary.Count;
            }
        }

        /// <summary>
        /// Return true
        /// </summary>
        public bool IsReadOnly
        {
            get { return true; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        #endregion ICollection<KeyValuePair<TKey,TValue>> Members

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        #endregion IEnumerable<KeyValuePair<TKey,TValue>> Members

        #region IEnumerable Members

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

        #endregion IEnumerable Members
    }
}