How can I get a KeyedCollection to be read-only? - c#

First let me explain why I'm using a KeyedCollection. I'm building a DLL and I have a list of items that I need to add to a collection and have them stay in the order I placed them but I also need to access them by both their index and by key (the key is a property of an object which I already defined). If there is any other simpler collection that does this, then please let me know.
Ok now, I need to be able to add items to this collection internally in the DLL but I need it to be publicly available to the DLL's end-user as read-only because I don't want them removing/altering the items I added.
I've searched all over this site, other sites, google in general and I have not been able to find a way to get some sort of read-only KeyedCollection. The closest I came was this page (http://www.koders.com/csharp/fid27249B31BFB645825BD9E0AFEA6A2CCDDAF5A382.aspx?s=keyedcollection#L28) but I couldn't quite get it to work.
UPDATE:
I took a look at those C5 classes. That, along with your other comments, helped me better understand how to create my own read-only class and it seems to work. However I have a problem when I try to cast the regular one to the read-only one. I get a compile-time cannot convert error. Here's the code I created (the first small class is what I originally had):
public class FieldCollection : KeyedCollection<string, Field>
{
protected override string GetKeyForItem(Field field)
{
return field.Name;
}
}
public class ReadOnlyFieldCollection : KeyedCollection<string, Field>
{
protected override string GetKeyForItem(Field field)
{ return field.Name; }
new public void Add(Field field)
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
new public void Clear()
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
new public void Insert(int index, Field field)
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
new public bool Remove(string key)
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
new public bool Remove(Field field)
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
new public bool RemoveAt(int index)
{ throw new ReadOnlyCollectionException("This collection is read-only."); }
}
If I have this variable defined:
private FieldCollection _fields;
then do this:
public ReadOnlyFieldCollection Fields;
Fields = (ReadOnlyFieldCollection)_fields;
it fails to compile. They're both inheriting from the same class, I thought they would be "compatible". How can I cast (or expose) the collection as the read-only type I just created?

I don't know of any built-in solution as well. This is an example of the suggestion I gave on the comment:
public class LockedDictionary : Dictionary<string, string>
{
public override void Add(string key, string value)
{
//do nothing or log it somewhere
//or simply change it to private
}
//and so on to Add* and Remove*
public override string this[string i]
{
get
{
return base[i];
}
private set
{
//...
}
}
}
You'll be able to iterate through the KeyValuePair list and everything else, but it won't be available for writing operations.
Just be sure to type it according to your needs.
EDIT
In order to have a sorted list, we can change the base class from Dictionary<T,T> to a Hashtable.
It would look like this:
public class LockedKeyCollection : System.Collections.Hashtable
{
public override void Add(object key, object value)
{
//do nothing or throw exception?
}
//and so on to Add* and Remove*
public override object this[object i]
{
get
{
return base[i];
}
set
{
//do nothing or throw exception?
}
}
}
Usage:
LockedKeyCollection aLockedList = new LockedKeyCollection();
foreach (System.Collections.DictionaryEntry entry in aLockedList)
{
//entry.Key
//entry.Value
}
Unfortunately, we can't change the access modifier of the methods, but we can override them to do nothing.

Edited to revise my original answer. Apparently, I've not had enough caffiene this AM.
You should look at the C5 collections library which offers read-only wrappers for collections and dictionaries, along with other stuff that the CLR team forgot:
http://www.itu.dk/research/c5/
You can also get the C5 Collections library via NuGet at http://www.nuget.org/packages/C5

There are two additional options available to you, which might better suit similar needs in the future. The first option is the simplest and requires the least amount of code; the second requires more scaffolding, but is optimal for sharing read-only members with an existing KeyedCollection<TKey, TValue> implementation.
Option 1: Derive from ReadOnlyCollection<TValue>
In this approach, you derive from ReadOnlyCollection and then add the this[TKey key] indexer. This is similar to the approach that #AndreCalil took above, except that ReadOnlyCollection already throws a NotSupportedException for each of the writable entry points, so you're covered there.
This is the approach Microsoft used internally with their ReadOnlyKeyedCollection<TKey, TValue> in the System.ServiceModel.Internals assembly (source):
class ReadOnlyKeyedCollection<TKey, TValue> : ReadOnlyCollection<TValue> {
KeyedCollection<TKey, TValue> innerCollection;
public ReadOnlyKeyedCollection(KeyedCollection<TKey, TValue> innerCollection) : base(innerCollection) {
Fx.Assert(innerCollection != null, "innerCollection should not be null");
this.innerCollection = innerCollection;
}
public TValue this[TKey key] {
get {
return this.innerCollection[key];
}
}
}
Option 2: Decorate an existing KeyedCollection<TKey, TValue>
The only real downside of the ReadOnlyCollection<TValue> approach is that it doesn't inherit any logic from your KeyedCollection<TKey, TValue> implementation. If you have a lot of custom logic, then you might instead choose to use a Decorator Pattern on your existing implementation. This would look something like:
class MyReadOnlyKeyedCollection : MyKeyedCollection, IReadOnlyCollection<TValue> {
MyKeyedCollection innerCollection;
public MyReadOnlyKeyedCollection(MyKeyedCollection innerCollection) : base() {
this.innerCollection = innerCollection;
}
protected override InsertItem(Int32 index, TItem item) {
throw new NotSupportedException("This is a read only collection");
}
//Repeat for ClearItems(), RemoveItem(), and SetItem()
public override void YourWriteMethod() {
throw new NotSupportedException("This is a read only collection");
}
//Repeat for any other custom writable members
protected override IList<T> Items {
get {
return.innerCollection.ToList();
}
}
//This should cover all other entry points for read operations
public override Object YourReadMethod() {
this.innerCollection.YourReadMethod();
}
//Repeat for any other custom read-only members
}
That's obviously a lot more code to write, however, and really only makes sense if you have a fair amount of custom code on your existing KeyedCollection<TKey, TItem> interface; otherwise, the first approach makes a lot more sense. Given that, it may instead be preferable to centralize that logic via either extension methods or, in C# 8.0, default interface methods.

It's never too late for an answer!
The simplest path:
IReadOnlyCollection<T>
the minimal setup to get it to work, i.e. no comparer and internal dictionary
Code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Z
{
public abstract class ReadOnlyKeyedCollection<TKey, TValue> : IReadOnlyCollection<TValue>
{
private readonly IReadOnlyCollection<TValue> _collection;
protected ReadOnlyKeyedCollection([NotNull] IReadOnlyCollection<TValue> collection)
{
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
}
public TValue this[[NotNull] TKey key]
{
get
{
if (key == null)
throw new ArgumentNullException(nameof(key));
foreach (var item in _collection)
{
var itemKey = GetKeyForItem(item);
if (Equals(key, itemKey))
return item;
}
throw new KeyNotFoundException();
}
}
#region IReadOnlyCollection<TValue> Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<TValue> GetEnumerator()
{
return _collection.GetEnumerator();
}
public int Count => _collection.Count;
#endregion
public bool Contains([NotNull] TKey key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return _collection.Select(GetKeyForItem).Contains(key);
}
protected abstract TKey GetKeyForItem(TValue item);
}
}

Related

Efficiently return IList<Interface> from List<T> (avoid casting from List<T> to List<I>)

I have the following code:
public interface ISomeObject
{
IList<ISomeObject> Objects { get; }
}
public class SomeObject : ISomeObject
{
public SomeObject()
{
Objects = new List<SomeObject>();
}
public List<SomeObject> Objects
{
get;
set;
}
IList<ISomeObject> ISomeObject.Objects
{
get
{
// What to do here?
// return Objects; // This doesn't work
return Objects.Cast<ISomeObject>().ToList(); // Works, but creates a copy each time.
}
}
SomeObject has a public property Objects that returns a List of class type. Clients knowing that class type can use that to do whatever they want. Clients only knowing about ISomeObject can use the Objects property only to get an IList<ISomeObject>. Because it is not allowed to cast List<SomeObject> to IList<ISomeObject> (due to the apple and banana issue) I need a way of converting that. The default way, using a Cast.ToList() works, but has the downside that it creates a new List each time the property is evaluated, which may be expensive. Changing ISomeObject.Objects to return an IEnumerable<ISomeObject> has the other downside that the client can't use indexing any more (which is quite relevant in my use case). And using Linq's ElementAt() call repeatedly is expensive, when used on an IEnumerable.
Has anybody got an idea on how to avoid either problem?
(of course, making SomeObject known everywhere is not an option).
You could/should implement a class similar to ReadOnlyCollection<T> to act as a proxy. Considering that it would be read only, it could be "covariant" (not language-side, but logically, meaning that it could proxy a TDest that is a subclass/interface of TSource) and then throw NotSupportedException() for all the write methods.
Something like this (code untested):
public class CovariantReadOlyList<TSource, TDest> : IList<TDest>, IReadOnlyList<TDest> where TSource : class, TDest
{
private readonly IList<TSource> source;
public CovariantReadOlyList(IList<TSource> source)
{
this.source = source;
}
public TDest this[int index] { get => source[index]; set => throw new NotSupportedException(); }
public int Count => source.Count;
public bool IsReadOnly => true;
public void Add(TDest item) => throw new NotSupportedException();
public void Clear() => throw new NotSupportedException();
public bool Contains(TDest item) => IndexOf(item) != -1;
public void CopyTo(TDest[] array, int arrayIndex)
{
// Using the nuget package System.Runtime.CompilerServices.Unsafe
// source.CopyTo(Unsafe.As<TSource[]>(array), arrayIndex);
// We love to play with fire :-)
foreach (TSource ele in source)
{
array[arrayIndex] = ele;
arrayIndex++;
}
}
public IEnumerator<TDest> GetEnumerator() => ((IEnumerable<TDest>)source).GetEnumerator();
public int IndexOf(TDest item)
{
TSource item2 = item as TSource;
if (ReferenceEquals(item2, null) && !ReferenceEquals(item, null))
{
return -1;
}
return source.IndexOf(item2);
}
public void Insert(int index, TDest item)
{
throw new NotSupportedException();
}
public bool Remove(TDest item)
{
throw new NotSupportedException();
}
public void RemoveAt(int index)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Use it like:
IList<string> strs = new List<string>();
IList<object> objs = new CovariantReadOlyList<string, object>(strs);
Changing ISomeObject.Objects to return an IEnumerable<ISomeObject> has the other downside that the client can't use indexing any more (which is quite relevant in my use case).
Indexing isn't just supported by the IList<T> interface, it's also supported by the IReadOnlyList<T> interface. Because IReadOnlyList<T> doesn't allow modification, it can be (and is) covariant just like IEnumerable<T> is.
So, just change the return type to IReadOnlyList<ISomeObject> and return the original list.
Of course, nothing prevents the caller from casting the result to List<SomeObject>, but the caller is supposed to have full access to that list anyway, so there is no security risk.
You may want try to encapsulate your List<SomeObject> making it an implementation detail and return IReadOnlyList<SomeObject> instead. Then SomeObject to ISomeObject cast want be unnecessary in interface implementation as well due to IReadOnlyList variance — you'll be able to return your Objects as IReadOnlyList<ISomeObject> .
Then just add some operations to mutate your underlying list like Add or Remove to container type if those are required.
Also I should mention that interfaces are not so good for restriction — evil consumer can easily cast your ISomeObject to SomeObject and do everything he wants, probably, you should reconsider your design. You'd better stick to such things as immutability and encapsulation for providing usable api. Explicitly use mutable builders then for immutable classes where it's reasonable.

ILookup with empty collections

Is there something inherently wrong with replacing
IDictionary<int, IEnumerable<string>>
with
ILookup<int, string>
I much prefer ILookup over IDictionary because of its more 'honest' interface and immutability.
However, I discovered that ILookup is unable to hold empty collections, so keys containing empty collections are simply do not exist in it. This is problem, because I also would like ILookup to convey information about all possible keys (even though some of them might be empty), so I can go like this:
var statistics = from grouping in myLookup
select new {grouping.Key, grouping.Count()};
which works with dictionary of enumerables, but unfortunately does not work with ILookup. It is just impossible to have entries where grouping.Count()==0, as with IDictionary.
As John Skeet states,
There’s one other important difference between a lookup and a dictionary: if you ask a lookup for the sequence corresponding to a key which it doesn’t know about, it will return an empty sequence, rather than throwing an exception. (A key which the lookup does know about will never yield an empty sequence.)
Now, what is wrong if ILookup allowed empty groupings? In order to have the best of both worlds I am about to add Filter() extension method for ILookup that does just this, but need to resolve a problem that Linq does not allow to create empty IGroupings (so I have to implement my own class), but I feel that maybe I am doing something against design principles of Linq.
Example
Two options:
1) you could create a nice, straightforward singleton-esque EmptyLookup class as follows:
var empty = EmptyLookup<int, string>.Instance;
// ...
public static class EmptyLookup<TKey, TElement>
{
private static readonly ILookup<TKey, TElement> _instance
= Enumerable.Empty<TElement>().ToLookup(x => default(TKey));
public static ILookup<TKey, TElement> Instance
{
get { return _instance; }
}
}
2) You can create a singleton class for empty lookups.
public sealed class EmptyLookup<T, K> : ILookup<T, K>
{
private static readonly EmptyLookup<T, K> _instance
= new EmptyLookup<T, K>();
public static EmptyLookup<T, K> Instance
{
get { return _instance; }
}
private EmptyLookup() { }
public bool Contains(T key)
{
return false;
}
public int Count
{
get { return 0; }
}
public IEnumerable<K> this[T key]
{
get { return Enumerable.Empty<K>(); }
}
public IEnumerator<IGrouping<T, K>> GetEnumerator()
{
yield break;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
yield break;
}
}
then you can write code like this:
var x = EmptyLookup<int, int>.Instance;
/*The benefit of creating a new class is that you can use the "is" operator and check for type equality:*/
if (x is EmptyLookup<,>) {
// ....
}
There is no wrong in keeping empty groupings is lookup, it's just that lookup does not support it because of it's nature in Linq.
You have to create an extension method by yourself.

C# IDictionary.Keys and IDictionary.Values: what is the most optimal implementation?

I have a C# class that acts as a dictionary so I'm now in the process of supporting IDictionary.
Everything is fine except for the properties Keys and Values:
ICollection<TKey> Keys { get; }
ICollection<TValue> Values { get; }
I don't have a collection of keys or values internally so I'm wondering how to provide these as a ICollection.
My first attempt was to use the magic of "yield return" like this:
ICollection<TValue> Values {
get {
for( int i = 0; i < nbValues; ++i ) {
yield return GetValue(i);
}
}
}
But of course this doesn't work since the returned type is not a IEnumerator but a ICollection...
It's too bad because this would have been the simplest solution !
My second attempt was to copy my values in a newly created array and return the array.
ICollection<TValue> Values {
get {
TValue[] copy = new TValue[nbValues];
for( int i = 0; i < nbValues; ++i ) {
copy[i] = GetValue(i);
}
return copy;
}
}
This would work since Array supports ICollection.
But the problem is that ICollection has methods to add and remove entries.
If the caller calls these methods only the copy will be modified not the dictionary...
The final solution I chose is to have my dictionary supports IDictionary but also ICollection and ICollection just so that I can return these collections from the properties Keys and Values...
public class MyDictionary : IDictionary<TKey,TValue>,
ICollection<TKey>,
ICollection<TValue>
{
}
So now the get accessor for the properties Keys and Values simply returns "this" ie: the dictionary.
ICollection<TValue> Values {
get {
return this;
}
}
It's probably the most optimal solution but I found it cumbersome to have to implement two extra interfaces whenever you want to implement IDictionary.
Do you have any other idea ?
I'm thinking that maybe returning the copy as an array was not such a bad idea after all. Anyway there is already a Add and Remove method in IDictionary which make more sense to be used.
Maybe returning a ReadOnlyCollection wrapping the array would be better as any attempt to modify the returned collection would fail?
ICollection<TValue> Values {
get {
TValue[] copy = new TValue[nbValues];
for( int i = 0; i < nbValues; ++i ) {
copy[i] = GetValue(i);
}
return new System.Collections.ObjectModel.ReadOnlyCollection<TValue>(copy);
}
}
I wouldn't personally expect you to be able to remove keys and values from a dictionary via Keys and Values anyway - I think it's fine to not do so.
Returning a ReadOnlyCollection<T> is fine - that waythe caller will just get an exception if they try to modify the collection, rather than the attempt just being silently ignored.
That exception follows the behaviour of Dictionary<TKey, TValue> by the way:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
IDictionary<string, string> dictionary =
new Dictionary<string, string> {{ "a", "b" }};
dictionary.Keys.Clear();
Console.WriteLine(dictionary.Count);
}
}
Results:
Unhandled Exception: System.NotSupportedException: Mutating a key collection
derived from a dictionary is not allowed.
at System.Collections.Generic.Dictionary`2
.KeyCollection.System.Collections.Generic.ICollection<TKey>.Clear()
at Test.Main()
As SLaks says, if you can create your own implementation of ICollection<T> which is lazy, that would be better - but if that's tricky for some reason, or indeed if the performance isn't important in your case, just creating the array and wrapping it in ReadOnlyCollection<T> is fine. You should consider documenting the expected performance either way though.
One thing to note if you do create your own lazy implementation: you should probably have some sort of "version number" to make sure that you invalidate the returned collection if the underlying data is changed.
A ReadOnlyCollection is the best approach of the options you listed; these collections are not supposed to be writable.
However that your getter is O(n), which is not good.
The correct approach is to create your own collection classes that implement ICollection<T> and return a live view of the dictionary. (and throw exceptions from mutation methods)
This is the approach taken by Dictionary<TKey, TValue>; it ensures that the property getters are fast, and does not waste extra memory.
Thank you all for your answers.
So I ended up doing two utility classes that implement the two ICollection requested by the Keys and Values properties. I have a few dictionaries where I need to add support for IDictionary so I will be reusing them a few times:
Here is the class for the collection of keys:
public class ReadOnlyKeyCollectionFromDictionary< TDictionary, TKey, TValue >
: ICollection<TKey>
where TDictionary : IDictionary<TKey,TValue>, IEnumerable<TKey>
{
IDictionary<TKey, TValue> dictionary;
public ReadOnlyKeyCollectionFromDictionary(TDictionary inDictionary)
{
dictionary = inDictionary;
}
public bool IsReadOnly {
get { return true; }
}
Here I implement ICollection<TKey> by simply calling the corresponding method on
the member "dictionary" but I throw a NotSupportedException for the methods Add,
Remove and Clear
public IEnumerator<TKey> GetEnumerator()
{
return (dictionary as IEnumerable<TKey>).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (dictionary as IEnumerable).GetEnumerator();
}
}
Here is the class for the collection of values:
public class ReadOnlyValueCollectionFromDictionary<TDictionary, TKey, TValue>
: ICollection<TValue>
where TDictionary : IDictionary<TKey, TValue>, IEnumerable<TValue>
{
IDictionary<TKey, TValue> dictionary;
public ReadOnlyValueCollectionFromDictionary(TDictionary inDictionary)
{
dictionary = inDictionary;
}
public bool IsReadOnly {
get { return true; }
}
Here I implement ICollection<TValue> by simply calling the corresponding method on
the member "dictionary" but I throw a NotSupportedException for the methods Add,
Remove and Clear
// I tried to support this one but I cannot compare a TValue with another TValue
// by using == since the compiler doesn't know if TValue is a struct or a class etc
// So either I add a generic constraint to only support classes (or ?) or I simply
// don't support this method since it's ackward in a dictionary anyway to search by
// value. Users can still do it themselves if they insist.
bool IEnumerable<TValue>.Contains(TValue value)
{
throw new System.NotSupportedException("A dictionary is not well suited to search by values");
}
public IEnumerator<TValue> GetEnumerator()
{
return (dictionary as IEnumerable<TValue>).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (dictionary as IEnumerable).GetEnumerator();
}
}
Then if my dictionary supports the IEnumerable for TKey and TValue everything becomes so simple:
public class MyDictionary : IDictionary<SomeKey,SomeValue>,
IEnumerable<SomeKey>,
IEnumerable<SomeValue>
{
IEnumerator<SomeKey> IEnumerable<SomeKey>.GetEnumerator()
{
for ( int i = 0; i < nbElements; ++i )
{
yield return GetKeyAt(i);
}
}
IEnumerator<SomeValue> IEnumerable<SomeValue>.GetEnumerator()
{
for ( int i = 0; i < nbElements; ++i )
{
yield return GetValueAt(i);
}
}
// IEnumerator IEnumerable.GetEnumerator() is already implemented in the dictionary
public ICollection<SomeKey> Keys
{
get
{
return new ReadOnlyKeyCollectionFromDictionary< MyDictionary, SomeKey, SomeValue>(this);
}
}
public ICollection<Value> Values
{
get
{
return new ReadOnlyValueCollectionFromDictionary< MyDictionary, SomeKey, SomeValue >(this);
}
}
}
It's too bad IDictionary is not returning IEnumerable instead of ICollection for the properties Keys and Values. All this would have been so much easier !

How to get a ReadOnlyCollection<T> of the Keys in a Dictionary<T, S>

My class contains a Dictionary<T, S> dict, and I want to expose a ReadOnlyCollection<T> of the keys. How can I do this without copying the Dictionary<T, S>.KeyCollection dict.Keys to an array and then exposing the array as a ReadOnlyCollection?
I want the ReadOnlyCollection to be a proper wrapper, ie. to reflect changes in the underlying Dictionary, and as I understand it copying the collection to an array will not do this (as well as seeming inefficient - I don't actually want a new collection, just to expose the underlying collection of keys...). Any ideas would be much appreciated!
Edit: I'm using C# 2.0, so don't have extension methods such as .ToList (easily) available.
If you really want to use ReadOnlyCollection<T>, the issue is that the constructor of ReadOnlyCollection<T> takes an IList<T>, while the KeyCollection of the Dictionary is only a ICollection<T>.
So if you want to wrap the KeyCollection in a ReadOnlyCollection, you'll have to create an adapter (or wrapper) type, implementing IList<T>, wrapping the KeyCollection. So it would look like:
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
Not very elegant though, especially as the KeyCollection is already a readonly collection, and that you could simply pass it around as an ICollection<T> :)
DrJokepu said that it might be difficult to implement a wrapper for Keys Collection. But, in this particular case, I think the implementation is not so difficult because, as we know, this is a read-only wrapper.
This allows us to ignore some methods that, in other case, would be hard to implement.
Here's a quick implementation of the wrapper for Dictionary.KeyCollection :
class MyListWrapper<T, TValue> : IList<T>
{
private Dictionary<T, TValue>.KeyCollection keys;
public MyListWrapper(Dictionary<T, TValue>.KeyCollection keys)
{
this.keys = keys;
}
#region IList<T> Members
public int IndexOf(T item)
{
if (item == null)
throw new ArgumentNullException();
IEnumerator<T> e = keys.GetEnumerator();
int i = 0;
while (e.MoveNext())
{
if (e.Current.Equals(item))
return i;
i++;
}
throw new Exception("Item not found!");
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public T this[int index]
{
get
{
IEnumerator<T> e = keys.GetEnumerator();
if (index < 0 || index > keys.Count)
throw new IndexOutOfRangeException();
int i = 0;
while (e.MoveNext() && i != index)
{
i++;
}
return e.Current;
}
set
{
throw new NotImplementedException();
}
}
#endregion
#region ICollection<T> Members
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
return keys.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
keys.CopyTo(array, arrayIndex);
}
public int Count
{
get { return keys.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
}
This might not be the best implementation for these methods :) but it was just for proving that this might be done.
Assuming you are using C# 3.0 and you have:
Dictionary< T,S > d;
Then
ReadOnlyCollection< T > r = new ReadOnlyCollection< T >( d.Keys.ToList() );
You will also need to import the System.Linq namespace.
Unfortunately you cannot to that direcly as far as I know as KeyCollection<T> does not expose anything that would allow you to do this easily.
You could, however, subclass ReadOnlyCollection<T> so that its constructor receives the dictionary itself and override the appropriate methods so that it exposes the Dictionary's items as if they were its own items.
For the record, in .NET 4.6, the KeyCollection<T> implements IReadOnlyCollection<T>, so if you use that interface, you can still reflect changes to the dictionary, still get O(1) contains*, and because the interface is covariant, you can return IReadOnlyCollection<some base type>
*Enumerable.Contains<T> does an as cast on the IEnumerable to forward it to ICollection<T>.Contains if available. See "remarks" on Enumerable.Contains: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.contains#system-linq-enumerable-contains-1(system-collections-generic-ienumerable((-0))-0). Dictionary.KeyCollection also implements ICollection<T>
It's ugly, but this will do it
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));

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

Is there anything built into the core C# libraries that can give me an immutable Dictionary?
Something along the lines of Java's:
Collections.unmodifiableMap(myMap);
And just to clarify, I am not looking to stop the keys / values themselves from being changed, just the structure of the Dictionary. I want something that fails fast and loud if any of IDictionary's mutator methods are called (Add, Remove, Clear).
No, but a wrapper is rather trivial:
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
IDictionary<TKey, TValue> _dict;
public ReadOnlyDictionary(IDictionary<TKey, TValue> backingDict)
{
_dict = backingDict;
}
public void Add(TKey key, TValue value)
{
throw new InvalidOperationException();
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return _dict.Keys; }
}
public bool Remove(TKey key)
{
throw new InvalidOperationException();
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return _dict.Values; }
}
public TValue this[TKey key]
{
get { return _dict[key]; }
set { throw new InvalidOperationException(); }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
throw new InvalidOperationException();
}
public void Clear()
{
throw new InvalidOperationException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dict.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dict.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _dict.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new InvalidOperationException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)_dict).GetEnumerator();
}
}
Obviously, you can change the this[] setter above if you want to allow modifying values.
As far as I know, there is not. But maybe you can copy some code (and learn a lot) from these articles:
Immutability in C# Part One: Kinds of Immutability
Immutability in C# Part Two: A Simple Immutable Stack
Immutability in C# Part Three: A Covariant Immutable Stack
Immutability in C# Part Four: An Immutable Queue
Immutability in C# Part Five: LOLZ
Immutability in C# Part Six: A Simple Binary Tree
Immutability in C# Part Seven: More on Binary Trees
Immutability in C# Part Eight: Even More On Binary Trees
Immutability in C# Part Nine: Academic? Plus my AVL tree implementation
Immutability in C# Part 10: A double-ended queue
Immutability in C# Part Eleven: A working double-ended queue
With the release of .NET 4.5, there is a new ReadOnlyDictionary class. You simply pass an IDictionary to the constructor to create the immutable dictionary.
Here is a helpful extension method which can be used to simplify creating the readonly dictionary.
I know this is a very old question, but I somehow found it in 2020 so I suppose it may be worth noting that there is a way to create immutable dictionary now:
https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutabledictionary.toimmutabledictionary?view=netcore-3.1
Usage:
using System.Collections.Immutable;
public MyClass {
private Dictionary<KeyType, ValueType> myDictionary;
public ImmutableDictionary<KeyType, ValueType> GetImmutable()
{
return myDictionary.ToImmutableDictionary();
}
}
Adding onto dbkk's answer, I wanted to be able to use an object initializer when first creating my ReadOnlyDictionary. I made the following modifications:
private readonly int _finalCount;
/// <summary>
/// Takes a count of how many key-value pairs should be allowed.
/// Dictionary can be modified to add up to that many pairs, but no
/// pair can be modified or removed after it is added. Intended to be
/// used with an object initializer.
/// </summary>
/// <param name="count"></param>
public ReadOnlyDictionary(int count)
{
_dict = new SortedDictionary<TKey, TValue>();
_finalCount = count;
}
/// <summary>
/// To allow object initializers, this will allow the dictionary to be
/// added onto up to a certain number, specifically the count set in
/// one of the constructors.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(TKey key, TValue value)
{
if (_dict.Keys.Count < _finalCount)
{
_dict.Add(key, value);
}
else
{
throw new InvalidOperationException(
"Cannot add pair <" + key + ", " + value + "> because " +
"maximum final count " + _finalCount + " has been reached"
);
}
}
Now I can use the class like so:
ReadOnlyDictionary<string, string> Fields =
new ReadOnlyDictionary<string, string>(2)
{
{"hey", "now"},
{"you", "there"}
};
The open-source PowerCollections library includes a read-only dictionary wrapper (as well as read-only wrappers for pretty much everything else), accessible via a static ReadOnly() method on the Algorithms class.
I don't think so. There is a way to create a read-only List and read only Collection, but I don't think there's a built in read only Dictionary. System.ServiceModel has a ReadOnlyDictinoary implementation, but its internal. Probably wouldn't be too hard to copy it though, using Reflector, or to simply create your own from scratch. It basically wraps an Dictionary and throws when a mutator is called.
One workaround might be, throw a new list of KeyValuePair from the Dictionary to keep the original unmodified.
var dict = new Dictionary<string, string>();
dict.Add("Hello", "World");
dict.Add("The", "Quick");
dict.Add("Brown", "Fox");
var dictCopy = dict.Select(
item => new KeyValuePair<string, string>(item.Key, item.Value));
// returns dictCopy;
This way the original dictionary won't get modified.
"Out of the box" there is not a way to do this. You can create one by deriving your own Dictionary class and implementing the restrictions you need.
I've found an implementation of an Inmutable (not READONLY) implementation of a AVLTree for C# here.
An AVL tree has logarithmic (not constant) cost on each operation, but stills fast.
http://csharpfeeds.com/post/7512/Immutability_in_Csharp_Part_Nine_Academic_Plus_my_AVL_tree_implementation.aspx
You could try something like this:
private readonly Dictionary<string, string> _someDictionary;
public IEnumerable<KeyValuePair<string, string>> SomeDictionary
{
get { return _someDictionary; }
}
This would remove the mutability problem in favour of having your caller have to either convert it to their own dictionary:
foo.SomeDictionary.ToDictionary(kvp => kvp.Key);
... or use a comparison operation on the key rather than an index lookup, e.g.:
foo.SomeDictionary.First(kvp => kvp.Key == "SomeKey");
In general it is a much better idea to not pass around any dictionaries in the first place (if you don't HAVE to).
Instead - create a domain-object with an interface that doesn't offer any methods modifying the dictionary (that it wraps). Instead offering required LookUp-method that retrieves element from the dictionary by key (bonus is it makes it easier to use than a dictionary as well).
public interface IMyDomainObjectDictionary
{
IMyDomainObject GetMyDomainObject(string key);
}
internal class MyDomainObjectDictionary : IMyDomainObjectDictionary
{
public IDictionary<string, IMyDomainObject> _myDictionary { get; set; }
public IMyDomainObject GetMyDomainObject(string key) {.._myDictionary .TryGetValue..etc...};
}
Since Linq, there is a generic interface ILookup.
Read more in MSDN.
Therefore, To simply get immutable dictionary you may call:
using System.Linq;
// (...)
var dictionary = new Dictionary<string, object>();
// (...)
var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value);
There's also another alternative as I have described at:
http://www.softwarerockstar.com/2010/10/readonlydictionary-tkey-tvalue/
Essentially it's a subclass of ReadOnlyCollection>, which gets the work done in a more elegant manner. Elegant in the sense that it has compile-time support for making the Dictionary read-only rather than throwing exceptions from methods that modify the items within it.

Categories