4

I'm wondering if I can create a Property that returns a read-only Dictionary?

Example:

    private readonly Dictionary<string, IMyObject> _myDictionary;
    public Dictionary<string, IMyObject> MyDictionary
    {
        get { return _myDictionary; }
    }

Therefore people using MyDictionary are not allowed to add, remove, or change items. Any way in which this can be done?

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Carra
  • 17,808
  • 7
  • 62
  • 75
  • Are you asking if only the set of keys cannot be changed, but the values of a given key can be? Or are you asking if there is a way to treat entire contents of the dictionary as read-only? – StayOnTarget Oct 03 '18 at 14:06
  • Since I asked this question almost ten years ago I'm no longer sure :) – Carra Oct 10 '18 at 11:47
  • np totally understandable! I've edited the question to convey what seems to have been the interpretation in the answers. – StayOnTarget Oct 11 '18 at 10:41

6 Answers6

7

I think that you'll need a class that wraps a Dictionary like the ReadOnlyCollection wraps a List.

While you will not find a default class that does this, you'll find an implementation in one of the answers to this question.

The BCL Extras Project also contains such an implementation. It supports the creation of a proxy object which implements IDictionary and can be used in its place.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
luvieere
  • 37,065
  • 18
  • 127
  • 179
2

.Net 4.5: System.Collections.ObjectModel.ReadOnlyDictionary

pavel
  • 21
  • 1
1

Inherit from System.Collections.ObjectModel.KeyedCollection<TKey,TItem>

Override InsertItem and RemoveItem

leppie
  • 115,091
  • 17
  • 196
  • 297
0

C# doesn't provide a way of doing this exactly the way you suggest, but you could always return a "home-made" immutable dictionary that wraps your myDictionary.

Have a look at this for more info on creating an immutable dictionary.

Does C# have a way of giving me an immutable Dictionary?

Community
  • 1
  • 1
Rob Levine
  • 40,328
  • 13
  • 85
  • 111
0

If the intent of providing this immutable dictionary is to protect your own dictionary, just give them a shallow copy.

public Dictionary<string, IMyObject> MyDictionary 
{ 
    get { return new Dictionary<string, IMyObject>(_myDictionary); } 
} 

Caller may add and remove whatever, but it won't matter for your dictionary.

Of course, the caller still has access to the things in the dictionary and may mutate them. If that's a problem, make a deep copy.

Amy B
  • 108,202
  • 21
  • 135
  • 185
0

Here's a implementation that I use for all those who need a ready to got snippet

/// <summary>
/// Read only wrapper for generics based dictionary.
/// Only provides lookup retrieval abilities.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class DictionaryReadOnly<TKey, TValue> : IDictionary<TKey, TValue>
{
    #region Private Members

    private IDictionary<TKey, TValue> _item;
    private bool _throwOnWritableAction = false;

    #endregion

    #region Constructors

    /// <summary>
    /// Constructor requiring the generic dictionary being wrapped.
    /// </summary>
    /// <param name="item"></param>
    public DictionaryReadOnly(IDictionary<TKey, TValue> items)
    {
        _throwOnWritableAction = true;
        _item = items;
    }

    /// <summary>
    /// Constructor requiring the generic dictionary being wrapped.
    /// </summary>
    /// <param name="item"></param>
    public DictionaryReadOnly(IDictionary<TKey, TValue> items, bool throwOnWritableAction)
    {            
        _throwOnWritableAction = throwOnWritableAction;
        _item = items;
    }
    #endregion

    #region IDictionary<TKey,TValue> Members

    /// <summary>
    /// Number of items in the dictionary.
    /// </summary>
    public int Count
    {
        get { return _item.Count; }
    }

    /// <summary>
    /// Determine if the underlying collection contains the key.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool ContainsKey(TKey key)
    {
        return _item.ContainsKey(key);
    }

    /// <summary>
    /// Returns the value associated with the key.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public TValue this[TKey key]
    {
        get { return _item[key]; }
        set
        {
            CheckAndThrow("Set");
        }
    }

    /// <summary>
    /// Return keys.
    /// </summary>
    public ICollection<TKey> Keys
    {
        get { return _item.Keys; }
    }

    /// <summary>
    /// Not-supported.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void Add(TKey key, TValue value)
    {
        CheckAndThrow("Add");
    }

    /// <summary>
    /// Not-supported.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool Remove(TKey key)
    {
        CheckAndThrow("Remove");
        return false;
    }

    /// <summary>
    /// Try to get the value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public bool TryGetValue(TKey key, out TValue value)
    {
        value = default(TValue);

        if (_item.ContainsKey(key))
        {
            value = _item[key];
            return true;
        }
        return false;
    }

    /// <summary>
    /// Get the values.
    /// </summary>
    public ICollection<TValue> Values
    {
        get { return _item.Values; }
    }

    #endregion

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

    /// <summary>
    /// Not-supported.
    /// </summary>
    /// <param name="item"></param>
    public void Add(KeyValuePair<TKey, TValue> item)
    {
        CheckAndThrow("Add");
    }

    /// <summary>
    /// Not-Supported.
    /// </summary>
    public void Clear()
    {
        CheckAndThrow("Clear");
    }

    /// <summary>
    /// Determine whether key value pair is in dictionary.
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _item.Contains(item);
    }

    /// <summary>
    /// Copy items to the array.
    /// </summary>
    /// <param name="array"></param>
    /// <param name="arrayIndex"></param>
    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        this._item.CopyTo(array, arrayIndex);
    }

    /// <summary>
    /// Indicate read-only
    /// </summary>
    public bool IsReadOnly
    {
        get { return true; }
    }

    /// <summary>
    /// Non-supported action.
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        CheckAndThrow("Remove");
        return false;
    }

    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members
    /// <summary>
    /// Get the enumerator.
    /// </summary>
    /// <returns></returns>
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _item.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members
    /// <summary>
    /// Get the enumerator.
    /// </summary>
    /// <returns></returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return _item.GetEnumerator();
    }

    #endregion

    /// <summary>
    /// Check and thrown based on flag.
    /// </summary>
    /// <param name="action"></param>
    void CheckAndThrow(string action)
    {
        if (_throwOnWritableAction)
            throw new InvalidOperationException("Can not perform action : " + action + " on this read-only collection.");
    }
}
sra
  • 23,820
  • 7
  • 55
  • 89