189

I'm returning a reference to a dictionary in my read only property. How do I prevent consumers from changing my data? If this were an IList I could simply return it AsReadOnly. Is there something similar I can do with a dictionary?

Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
    Get
        Return _mydictionary
    End Get
End Property
Rob Sobers
  • 20,737
  • 24
  • 82
  • 111
  • 4
    There must be some way of doing it, or else there wouldn't be a IsReadOnly property on IDictionary... ( http://msdn.microsoft.com/en-us/library/bb338949.aspx ) – Powerlord Mar 24 '09 at 17:30
  • 2
    Many of the conceptual benefits of immutability can be gained without runtime enforcing it. If this is a private project, consider a disciplined, informal method. If you must provide data to a consumer, you should seriously consider deep copies. When you consider that an immutable collection requires 1) immutable reference to the collection 2) preventing mutating the sequence itself and 3) preventing modifying the properties on the items of the collection, and that some of these can be violated by reflection, runtime enforcement is not practical. – Sprague May 30 '12 at 12:32
  • 27
    Since .NET 4.5, There is a [System.Collections.ObjectModel.ReadOnlyDictionary](http://msdn.microsoft.com/en-us/library/gg712875.aspx) ^_^ – Smartkid Oct 25 '12 at 15:56
  • 2
    There is also now Microsoft Immutable Collections via NuGet http://msdn.microsoft.com/en-us/library/dn385366%28v=vs.110%29.aspx – VoteCoffee Jan 09 '14 at 16:42
  • VoteCoffee's link to the Immutable NuGet package is no longer valid.See: https://www.nuget.org/packages/System.Collections.Immutable/ – John Pankowicz Nov 23 '20 at 13:55

15 Answers15

230

.NET 4.5

The .NET Framework 4.5 BCL introduces ReadOnlyDictionary<TKey, TValue> (source).

As the .NET Framework 4.5 BCL doesn't include an AsReadOnly for dictionaries, you will need to write your own (if you want it). It would be something like the following, the simplicity of which perhaps highlights why it wasn't a priority for .NET 4.5.

public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary)
{
    return new ReadOnlyDictionary<TKey, TValue>(dictionary);
}

.NET 4.0 and below

Prior to .NET 4.5, there is no .NET framework class that wraps a Dictionary<TKey, TValue> like the ReadOnlyCollection wraps a List. However, it is not difficult to create one.

Here is an example - there are many others if you Google for ReadOnlyDictionary.

Steven
  • 166,672
  • 24
  • 332
  • 435
Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
  • 7
    It doesn't look like they remembered to make an `AsReadOnly()` method on the usual `Dictionary<,>`, so I wonder how many people will discover their new type. This Stack Overflow thread will help, though. – Jeppe Stig Nielsen Aug 01 '12 at 22:23
  • @Jeppe: I doubt it has anything to do with remembering. Every feature costs and I doubt AsReadOnly was high on the priority list, especially since it's so easy to write. – Jeff Yates Aug 23 '12 at 13:31
  • 1
    Note that this is simply a wrapper; changes to the underlying dictionary (the one passed to the constructor) will still mutate the read-only dictionary. See also http://stackoverflow.com/questions/139592/what-is-the-best-way-to-clone-deep-copy-a-net-generic-dictionarystring-t – TrueWill Apr 22 '16 at 20:44
  • 1
    @JeffYates Considering how simple it is, writing it would have taken less time than deciding whether or not to spend time writing it. Because of that, my wager is on "they forgot". – Dan Bechard May 13 '16 at 18:34
  • As TrueWill stated, the underlying dictionary can still be mutated. You might want to consider passing a clone of the original dictionary to the constructor if you want true immutability (assuming key and value type are also immutable.) – user420667 Aug 25 '16 at 20:23
  • @user420667 "read only" is not the same is "immutability", but "immutability" implies "read only". Sometimes a read-only reference to an othwise mutable object is exactly what you want. – Sentry Dec 12 '16 at 12:15
157

Here's a simple implementation that wraps a dictionary:

public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private readonly IDictionary<TKey, TValue> _dictionary;

    public ReadOnlyDictionary()
    {
        _dictionary = new Dictionary<TKey, TValue>();
    }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }

    #region IDictionary<TKey,TValue> Members

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

    public bool ContainsKey(TKey key)
    {
        return _dictionary.ContainsKey(key);
    }

    public ICollection<TKey> Keys
    {
        get { return _dictionary.Keys; }
    }

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

    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]
    {
        get
        {
            return _dictionary[key];
        }
    }

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

    #endregion

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

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

    void ICollection<KeyValuePair<TKey, TValue>>.Clear()
    {
        throw ReadOnlyException();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _dictionary.Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _dictionary.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _dictionary.Count; }
    }

    public bool IsReadOnly
    {
        get { return true; }
    }

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

    #endregion

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

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dictionary.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

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

    #endregion

    private static Exception ReadOnlyException()
    {
        return new NotSupportedException("This dictionary is read-only");
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 11
    +1 for posting complete code and not just a link, but I'm curious, what's the point of an empty constructor in a ReadOnlyDictionary? :-) – Samuel Neff Feb 22 '11 at 16:36
  • 20
    Watch out for that constructor. If you do a reference copy of the dictionary passed in, it's possible for an outside piece of code to modify your "Read Only" dictionary. Your constructor should do a full, deep copy of the argument. – askheaves Sep 16 '11 at 01:13
  • 25
    @askheaves: Good observation, but it's actually quite often useful to use the original reference in the Read Only types - keep in your private variable the original and modify it for outside consumers. For instance, check out the ReadOnlyObservableCollection or the ReadOnlyCollection objects that are built in: Thomas provided something that works exactly like those inherent to the .Net framework. Thanks Thomas! +1 – Matt DeKrey Oct 17 '11 at 23:19
  • Wouldn't it be better to return something like `new ReadOnlyCollection(_dict.Keys.ToList())` and `new ReadOnlyCollection(_dict.Values.ToList())` for `Keys` and `Values`, respectively? However I don't like to use the extension method `ToList()` here, but `ReadOnlyCollection` has no constructor which takes an `ICollection`. – Robert Hegner Jan 10 '12 at 11:02
  • 1
    I would add that the constructor should probably build a new dictionary, even if it's inefficient. That way one can't accidentally modify the `ReadOnlyDictionary` by modifying the original dictionary. Additionally, I would add a comment that the keys/values can be modified, until we have support for read only objects, which I don't think would be all that hard to do. – user420667 Jan 24 '12 at 00:20
  • 13
    @user420667: the way it's implemented, it's a "read-only view of a non read-only dictionary". Some other code might change the content of the original dictionary, and these changes will be reflected in the read-only dictionary. It could be the desired behavior, or not, depending on what you want to achieve... – Thomas Levesque Jan 24 '12 at 00:25
  • 6
    @Thomas: That is the same as a ReadOnlyCollection in the .NET BCL. It is a read-only view on a possibly mutable collection. ReadOnly does not mean immutable and immutability should not be expected. – Jeff Yates Jun 29 '12 at 14:25
  • 7
    I would recommend implementing all NotSupported methods as explicit interface implementations, so that they are more difficult to call. – Zaid Masud Sep 05 '12 at 09:34
  • 1
    Are the `Values` and `Keys` collections generally read-only? If not, it would make sense to only return a view on these collections. – Jean Hominal Jan 25 '13 at 16:41
  • @JeanHominal, it depends on the type of the underlying dictionary, but if it's a `Dictionary` (which is the default in this implementation), then yes, these collections are read-only. – Thomas Levesque Jan 25 '13 at 22:42
  • 4
    I recommend putting `[Obsolete("Not Supported for ReadOnlyDictionaries", true)]` attributues in front of your implementations of Add, Remove, Clear, and this[] setter. This way, a caller will get a compile-time error when trying to use these methods. This is as opposed to the runtime-errors you'll get as-is. – user2023861 Feb 13 '14 at 13:55
  • 2
    @user2023861, these methods are already declared as explicit interface implementations, so they can't be called directly from a variable of type `ReadOnlyDictionary`. They can only be called from a variable of type `IDictionary`, and obviously I can't add these attributes on the interface... – Thomas Levesque Feb 13 '14 at 14:32
  • 2
    @askheaves [`ImmutableDictionary`](http://msdn.microsoft.com/en-us/library/dn467181(v=vs.111).aspx) – Timothy Shields Jul 15 '14 at 21:31
  • @askheaves As Timothy mentioned above, you are confusing "read-only" with "immutable". Immutable implies read-only, but read-only does _not_ imply immutable. It is easy to confuse them. – AnorZaken Apr 03 '16 at 20:57
  • 1
    this really violates LSP. clients could be working with an instance of your dict, only knowing it as a reference to a normal dict, and suddenly you throw an exception in their faces. prefer implementing a readonly interface and use composition instead to prevent lying in the api – sara May 06 '16 at 23:00
  • @kai, I don't think it violates LSP, because of the IsReadOnly property, which is part of the contract. Also, the read-only version of the interface didn't exist when I wrote this answer... – Thomas Levesque May 07 '16 at 17:23
  • @ThomasLevesque even if we accept that IsReadOnly arguably keeps it withing liskov, it's still really bad OOP to design interfaces around "query object and make decision based on it's state" rather than "let object expose actions and tell it what you want it to do". even an IsReadOnly property is dubious in that there's no guarantee (without reading docs) that it won't change between your reading it and your invoking a mutating method. another answer features a home-made readonly interface for dictionaries, I greatly prefer that solution. – sara May 07 '16 at 17:27
20

It was announced in the recent BUILD conference that since .NET 4.5, the interface System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> is included. The proof is here (Mono) and here (Microsoft) ;)

Not sure if ReadOnlyDictionary is included too, but at least with the interface it shouldn't be difficult to create now an implementation which exposes an official .NET generic interface :)

knocte
  • 16,941
  • 11
  • 79
  • 125
18

Feel free to use my simple wrapper. It does NOT implement IDictionary, so it doesn't have to throw exceptions at runtime for dictionary methods that would change the dictionary. Change methods simply aren't there. I made my own interface for it called IReadOnlyDictionary.

public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable
{
    bool ContainsKey(TKey key);
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    int Count { get; }
    bool TryGetValue(TKey key, out TValue value);
    TValue this[TKey key] { get; }
    bool Contains(KeyValuePair<TKey, TValue> item);
    void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex);
    IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
}

public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> _dictionary;
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }
    public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }
    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] { get { return _dictionary[key]; } }
    public bool Contains(KeyValuePair<TKey, TValue> item) { return _dictionary.Contains(item); }
    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _dictionary.CopyTo(array, arrayIndex); }
    public int Count { get { return _dictionary.Count; } }
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); }
}
Dale Barnard
  • 779
  • 1
  • 7
  • 16
  • 4
    +1 for not violating the `IDictionary` contract. I think it's more correct from an OOP perspective for `IDictionary` to inherit from `IReadOnlyDictionary`. – Sam May 17 '13 at 01:23
  • @Sam Agreed, and if we could go back I think it would be the best and most correct to to to have `IDictionary` (for current `IReadOnlyDictionary`) and `IMutableDictionary` (for current `IDictionary`). – MasterMastic Apr 10 '14 at 22:02
  • 1
    @MasterMastic That's a bizarre proposition. I don't recall any other built-in classes relying on the reverse assumption that an immutable collection is what a user expects by default. – Dan Bechard May 13 '16 at 18:37
11

IsReadOnly on IDictionary<TKey,TValue> is inherited from ICollection<T> (IDictionary<TKey,TValue> extends ICollection<T> as ICollection<KeyValuePair<TKey,TValue>>). It is not used or implemented in any way ( and is in fact "hidden" through the use of explicitly implementing the associated ICollection<T> members ).

There are at least 3 ways I can see to solve the problem:

  1. Implement a custom read only IDictionary<TKey, TValue> and wrap / delegate to an inner dictionary as has been suggested
  2. Return an ICollection<KeyValuePair<TKey, TValue>> set as read only or an IEnumerable<KeyValuePair<TKey, TValue>> depending on the use of the value
  3. Clone the dictionary using the copy constructor .ctor(IDictionary<TKey, TValue>) and return a copy - that way the user is free to do with it as they please and it does not impact on the state of the object hosting the source dictionary. Note that if the dictionary you are cloning contains reference types ( not strings as shown in the example ) you will need to do the copying "manually" and clone the reference types as well.

As an aside; when exposing collections, aim to expose the smallest possible interface - in the example case it should be IDictionary as this allows you to vary the underlying implementation without breaking the public contract that the type exposes.

Neal
  • 4,278
  • 2
  • 19
  • 25
8

A read-only dictionary can to some extent be replaced by Func<TKey, TValue> - I usually use this in an API if I only want people performing lookups; it's simple, and in particular, it's simple to replace the backend should you ever wish to. It doesn't provide the list of keys, however; whether that matters depends on what you're doing.

Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
4

No, but it would be easy to roll your own. IDictionary does define an IsReadOnly property. Just wrap a Dictionary and throw a NotSupportedException from the appropriate methods.

wekempf
  • 2,738
  • 15
  • 16
3

None available in the BCL. However I published a ReadOnlyDictionary (named ImmutableMap) in my BCL Extras Project

In addition to being a fully immutable dictionary, it supports producing a proxy object which implements IDictionary and can be used in any place where IDictionary is taken. It will throw an exception whenever one of the mutating APIs are called

void Example() { 
  var map = ImmutableMap.Create<int,string>();
  map = map.Add(42,"foobar");
  IDictionary<int,string> dictionary = CollectionUtility.ToIDictionary(map);
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 9
    Your ImmutableMap is implemented as a balanced tree. Since, in .NET, people generally expect a "dictionary" to be implemented via hashing--and exhibit the corresponding complexity properties--you might want to be careful about promoting ImmutableMap as a "dictionary." – Glenn Slayden Aug 14 '11 at 17:58
  • appears that the code.msdn.com sites is defunct. BCLextras now here https://github.com/scottwis/tiny/tree/master/third-party/BclExtras – BozoJoe Jul 01 '14 at 05:26
1

+1 Great job, Thomas. I took ReadOnlyDictionary one step further.

Much like Dale's solution, I wanted to remove Add(), Clear(), Remove(), etc from IntelliSense. But I wanted my derived objects to implement IDictionary<TKey, TValue>.

Furthermore, I would like the following code to break: (Again, Dale's solution does this too)

ReadOnlyDictionary<int, int> test = new ReadOnlyDictionary<int,int>(new Dictionary<int, int> { { 1, 1} });
test.Add(2, 1);  //CS1061

The Add() line results in:

error CS1061: 'System.Collections.Generic.ReadOnlyDictionary<int,int>' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument 

The caller can still cast it to IDictionary<TKey, TValue>, but the NotSupportedException will be raised if you try to use the non-read only members (from Thomas's solution).

Anyway, here's my solution for anyone that also wanted this:

namespace System.Collections.Generic
{
    public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        const string READ_ONLY_ERROR_MESSAGE = "This dictionary is read-only";

        protected IDictionary<TKey, TValue> _Dictionary;

        public ReadOnlyDictionary()
        {
            _Dictionary = new Dictionary<TKey, TValue>();
        }

        public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        {
            _Dictionary = dictionary;
        }

        public bool ContainsKey(TKey key)
        {
            return _Dictionary.ContainsKey(key);
        }

        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]
        {
            get { return _Dictionary[key]; }
            set { throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE); }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _Dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _Dictionary.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _Dictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _Dictionary.GetEnumerator();
        }

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

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

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

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

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

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }
    }
}
Liam
  • 27,717
  • 28
  • 128
  • 190
Robert H.
  • 5,864
  • 1
  • 22
  • 26
1

Now, there are Microsoft Immutable Collections (System.Collections.Immutable). Get them via NuGet.

Palec
  • 12,743
  • 8
  • 69
  • 138
VoteCoffee
  • 4,692
  • 1
  • 41
  • 44
1

You could create a class that only implements a partial implementation of the dictionary, and hides all the add/remove/set functions.

Use a dictionary internally that the external class passes all requests to.

However, since your dictionary is likely holding reference types, there is no way you ca stop the user from setting values on the classes held by the dictionary (unless those classes themselves are read only)

Jason Coyne
  • 6,509
  • 8
  • 40
  • 70
1

I don't think there's an easy way of doing it...if your dictionary is part of a custom class, you could achieve it with an indexer:

public class MyClass
{
  private Dictionary<string, string> _myDictionary;

  public string this[string index]
  {
    get { return _myDictionary[index]; }
  }
}
Jonas
  • 4,454
  • 3
  • 37
  • 45
  • I need to be able to expose the entire dictionary as well as an indexer. – Rob Sobers Mar 25 '09 at 01:21
  • This seems like a very good solution. However, the clients of class MyClass might need to known more about the dictionary, for instance, for iteration through it. And what if a key does not exist (might exposing TryGetValue() in some form be a good idea)? Can you make your answer and sample code more complete? – Peter Mortensen Aug 02 '16 at 10:39
0

This is a bad solution, see at bottom.

For those still using .NET 4.0 or earlier, I have a class that works just like the one in the accepted answer, but it's much shorter. It extends the existing Dictionary object, overriding (actually hiding) certain members to have them throw an exception when called.

If the caller tries to call Add, Remove, or some other mutating operation that the built-in Dictionary has, the compiler will throw an error. I use the Obsolete attributes to raise these compiler errors. This way, you can replace a Dictionary with this ReadOnlyDictionary and immediately see where any problems might be without having to run your application and waiting for run-time exceptions.

Take a look:

public class ReadOnlyException : Exception
{
}

public class ReadOnlyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        : base(dictionary) { }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        : base(dictionary, comparer) { }

    //The following four constructors don't make sense for a read-only dictionary

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity, IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }


    //Use hiding to override the behavior of the following four members
    public new TValue this[TKey key]
    {
        get { return base[key]; }
        //The lack of a set accessor hides the Dictionary.this[] setter
    }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Add(TKey key, TValue value) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Clear() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new bool Remove(TKey key) { throw new ReadOnlyException(); }
}

This solution has a problem pointed out by @supercat illustrated here:

var dict = new Dictionary<int, string>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
};

var rodict = new ReadOnlyDictionary<int, string>(dict);
var rwdict = rodict as Dictionary<int, string>;
rwdict.Add(4, "four");

foreach (var item in rodict)
{
    Console.WriteLine("{0}, {1}", item.Key, item.Value);
}

Rather than give a compile-time error like I expected, or a runtime-exception like I hoped, this code runs without error. It prints four numbers. That makes my ReadOnlyDictionary a ReadWriteDictionary.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2023861
  • 8,030
  • 9
  • 57
  • 86
  • The problem with that approach is that such an object may be passed to a method that expects a `Dictionary` without any compiler complaint, and casting or coercing a reference to the plain-dictionary type will remove any protections. – supercat May 14 '15 at 15:38
  • @supercat, crap, you're right. I thought I had a good solution too. – user2023861 May 14 '15 at 18:49
  • I remember making a derivative of `Dictionary` with a `Clone` method that chained to `MemberwiseClone`. Unfortunately, while it should be possible to efficiently clone a dictionary by cloning the backing stores, the fact that the backing stores are `private` rather than `protected` means there's no way for a derived class to clone them; using `MemberwiseClone` without also cloning the backing stores will mean that subsequent modifications made to the original dictionary will break the clone, and modifications made to the clone will break the original. – supercat May 14 '15 at 22:58
0

For anyone coming to this question using .NET Core, check out System.Collections.Immutable.

For immutable dictionaries specifically, you can use ImmutableDictionary<TKey, TValue>.Builder :

public static ImmutableDictionary<string, string> ToReadOnly(this IDictionary<String, string> d)
{
    var builder = new ImmutableDictionary<string, string>.Builder();
    builder.AddRange(d);
    return builder.ToImmutable();
}
crimbo
  • 10,308
  • 8
  • 51
  • 55
0
public IEnumerable<KeyValuePair<string, string>> MyDictionary()
{
    foreach(KeyValuePair<string, string> item in _mydictionary)
        yield return item;
}
Liam
  • 27,717
  • 28
  • 128
  • 190
shahkalpesh
  • 33,172
  • 3
  • 63
  • 88
  • 2
    Or you can do: `public IEnumerable> MyDictionary() { return _mydictionary; }` – Pat Sep 16 '10 at 21:22