Search code examples
c#genericscollectionsreadonly-collection

how to make accessor for Dictionary in a way that returned Dictionary cannot be changed C# / 2.0


I thought of solution below because the collection is very very small. But what if it was big?

private Dictionary<string, OfTable> _folderData = new Dictionary<string, OfTable>();

public Dictionary<string, OfTable> FolderData
{
    get { return new Dictionary<string,OfTable>(_folderData); }
}

With List you can make:

public class MyClass
{
    private List<int> _items = new List<int>();

    public IList<int> Items
    {
        get { return _items.AsReadOnly(); }
    }
}

That would be nice!

Thanks in advance, Cheers & BR - Matti

NOW WHEN I THINK THE OBJECTS IN COLLECTION ARE IN HEAP. SO MY SOLUTION DOES NOT PREVENT THE CALLER TO MODIFY THEM!!! CAUSE BOTH Dictionary s CONTAIN REFERENCES TO SAME OBJECT. DOES THIS APPLY TO List EXAMPLE ABOVE?

class OfTable
{
    private int _table;
    private List<int> _classes;
    private string _label;

    public OfTable()
    {
        _classes = new List<int>();
    }

    public int Table
    {
        get { return _table; }
        set { _table = value; }
    }

    public List<int> Classes
    {
        get { return _classes; }
        set { _classes = value; }
    }

    public string Label
    {
        get { return _label; }
        set { _label = value; }
    }
}

so how to make this immutable??


Solution

  • It's not difficult to roll your own ReadOnlyDictionary<K,V> wrapper class. Something like this:

    public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private readonly IDictionary<TKey, TValue> _dictionary;
    
        public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
    
            _dictionary = dictionary;
        }
    
        public bool ContainsKey(TKey key)
        {
            return _dictionary.ContainsKey(key);
        }
    
        public int Count
        {
            get { return _dictionary.Count; }
        }
    
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }
    
        public ICollection<TKey> Keys
        {
            get { return _dictionary.Keys; }
        }
    
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dictionary.TryGetValue(key, out value);
        }
    
        public ICollection<TValue> Values
        {
            get { return _dictionary.Values; }
        }
    
        public TValue this[TKey key]    // Item
        {
            get { return _dictionary[key]; }
        }
    
        #region IDictionary<TKey, TValue> Explicit Interface Implementation
    
        void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
        {
            throw new NotSupportedException("Dictionary is read-only.");
        }
    
        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            throw new NotSupportedException("Dictionary is read-only.");
        }
    
        TValue IDictionary<TKey, TValue>.this[TKey key]    // Item
        {
            get { return _dictionary[key]; }
            set { throw new NotSupportedException("Dictionary is read-only."); }
        }
    
        #endregion
    
        #region ICollection<T> Explicit Interface Implementation
    
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException("Collection is read-only.");
        }
    
        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            throw new NotSupportedException("Collection is read-only.");
        }
    
        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
        {
            return _dictionary.Contains(item);
        }
    
        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _dictionary.CopyTo(array, arrayIndex);
        }
    
        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get { return true; }
        }
    
        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException("Collection is read-only.");
        }
    
        #endregion
    
        #region IEnumerable Explicit Interface Implementation
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)_dictionary).GetEnumerator();
        }
    
        #endregion
    }
    

    If you're using C#3 or later then you could knock-up a matching AsReadOnly extension method too:

    public static class ReadOnlyDictionaryHelper
    {
        public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
        {
            var temp = dictionary as ReadOnlyDictionary<TKey, TValue>;
            return temp ?? new ReadOnlyDictionary<TKey, TValue>(dictionary);
        }
    }
    

    And then return the read-only wrapper from your property:

    // in C#2
    return new ReadOnlyDictionary<string, OfTable>(_folderData);
    
    // in C#3 or later
    return _folderData.AsReadOnly();