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.
Related
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.
I am trying to build a dictionary where the key is a property of the value object. However I would like to construct the value object in the dictionary's add method. Is there a way to do this without using an intermediate variable?
For example I would like to do the following, but of course the key value isn't available when needed.
Dictionary<int,SomeComplexObject> dict = new Dicionary<int,SomeComplexObject>{
{someComplexObject.Key, new SomeComplexObject {Key = 1, Name = "FooBar"},
{someComplexObject.Key, new SomeComplexObject {Key = 2, Name = "FizzBang"}
};
Do I have to do it this ugly way:
Dictionary<int,SomeComplexObject> dict = new Dicionary<int,SomeComplexObject>();
SomeComplexObject value1 = new SomeComplexObject{Key=1,Name = "FooBar"};
dict.Add(value1.Key,value1);
SomeComplexObject value2 = new SomeComplexObject{Key=2,Name = "FizzBang"};
dict.Add(value.Key,value2);
I don't think this is the same question as
How to use an object's identity as key for Dictionary<K,V>
because I am not asking specifically about the key of a dictionary but if there is a way to have access to a objects property when the object is not being created until later in the methods parameter list.
I don't think an extension method (as proposed in comments) is really what you want here, as it's only a partial solution. I.e. you would have to write a new extension method for each dictionary value type you wanted to use, which negates the whole point of asking for a general solution.
Instead, it seems to me that you probably just want to subclass the Dictionary<TKey, TValue> type to add your own custom behavior. You can do this just once, in a general-purpose way, so that you can provide a delegate for each type you expect to have to use this way.
That would look something like this:
class KeyExtractorDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
private readonly Func<TValue, TKey> _extractor;
public KeyExtractorDictionary(Func<TValue, TKey> extractor)
{
_extractor = extractor;
}
public void Add(TValue value)
{
Add(_extractor(value), value);
}
}
You would use it something like this:
class Data
{
public int Key { get; }
public string Name { get; }
public Data(int key, string name)
{
Key = key;
Name = name;
}
}
class Program
{
static void Main(string[] args)
{
KeyExtractorDictionary<int, Data> dictionary =
new KeyExtractorDictionary<int, Data>(d => d.Key)
{
new Data(1, "FooBar"),
new Data(2, "FizzBang")
};
}
}
(I used Data as the value type type, instead of T as you seem to have used in your question, to avoid confusing the type name with a generic type parameter.)
In this way, you only have to write the class once, regardless of how many different types you might want to use for this type of dictionary. You can then pass the class constructor the key extractor delegate appropriate for the current value type of the dictionary.
Note that doing it this way, you also can take advantage of C#'s collection initializer syntax. Since your new type has an Add() method that takes just the value for each dictionary entry, the compiler will translate a collection initializer into the correct calls to add each object to the dictionary.
This allows for a dictionary in which you can still retrieve objects solely by the key value (using a custom comparer would require an instance of the value type with the same key you're looking for), while still addressing the broader concerns of not having to specify the key explicitly when adding objects, and of generality and reuse.
You can try an extension method, which is less invasive:
public static void AddByKey<TKey, T>(this Dictionary<TKey, T> dictionary, T item)
{
dictionary.Add(item.Key, item);
}
But to really do this correctly you also need an interface to protect you against types without the Key property:
public interface ItemWithKey<TKey>
{
TKey Key { get; }
}
public static void AddByKey<TKey, T>(this Dictionary<TKey, T> dictionary, T item)
where T : ItemWithKey<TKey>
{
dictionary.Add(item.Key, item);
}
I don't have a compiler in my hands right now, I cannot test this code so minor errors may have slipped in. I hope you get the idea and usefulness if you have those cases a lot in your code. Otherwise, I'd advise to go with the ugly working code you already have.
You can use the ToDictionary() extension method to solve this issue. Here is a complete example that can be run in LINQPad.
void Main()
{
Dictionary<int, SomeComplextObject> dict = new List<SomeComplextObject>{
{new SomeComplextObject {Key = 1, Name = "FooBar"}},
{new SomeComplextObject {Key = 2, Name = "FizzBangr"}}
}.ToDictionary(k =>k.Key);
//Dump Dictionary to LINQPad's result window.
dict.Dump();
}
public class SomeComplextObject{
public int Key { get; set; }
public string Name {get;set;}
}
I would like to propose a different slightly different way of going about this, it's similar to #pid 's method but instead of an interface uses a linq expression. First built your list of objects, then use an extension method to add them to your dictionary in a single simple step. In my mind this is also a little more intuitive to read, your program would look like:
class Program
{
static void Main(string[] args)
{
List<SomeComplexObject> toAdd = new List<SomeComplexObject>() {
new SomeComplexObject(1,"FooBar"),
new SomeComplexObject(2,"FizzBang")
};
var dict = new Dictionary<int,SomeComplexObject>();
dict.AddByKey(toAdd, item => item.Key);
}
}
Where AddByKey is an extension method that uses linq to basically pass a reference to that property and would look like this:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
public static class DictionaryExtensions
{
/// <summary>
/// This extension method was built for when you want to add a list of items to a dictionary as the values, and you want to use one of those
/// items' properties as the key. It uses LINQ to check by property reference.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dict"></param>
/// <param name="targets"></param>
/// <param name="propertyToAdd"></param>
public static void AddByKey<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<TValue> targets, Expression<Func<TValue, TKey>> propertyToAdd)
{
MemberExpression expr = (MemberExpression)propertyToAdd.Body;
PropertyInfo prop = (PropertyInfo)expr.Member;
foreach (var target in targets)
{
var value = prop.GetValue(target);
if (!(value is TKey))
throw new Exception("Value type does not match the key type.");//shouldn't happen.
dict.Add((TKey)value, target);
}
}
}
And if you wanted to simplify the calling code even further, instead of returning void, you could have that extension method return the originally passed dictionary, and then your calling code could be collapsed to:
var dict = new Dictionary<int,SomeComplexObject>().AddByKey(toAdd, item => item.Key);
I have an IDictionary<string, MyEnum?> collection that needs to be passed to a class to wrap it in a IReadOnlyDictionary<string, MyEnum> (note MyEnum but not MyEnum?).
I have come up with two designs:
Delay the wrapping to IReadOnlyDictionary<string, MyEnum> until property access:
public class MyClass
{
private readonly IEnumerable<KeyValuePair<string, MyEnum?>> _kvps;
public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
_kvps = kvps;
}
public IReadOnlyDictionary<string, MyEnum> Kvps
{
get
{
var filtered = from kvp in _kvps
where kvp.Value.HasValue
select kvp;
return new ReadOnlyDictionary<string, MyEnum>(
filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
}
}
}
Eagerly evaluate the collection in constructor
public class MyClass
{
public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
Kvps = ToReadOnly(kvps);
}
public IReadOnlyDictionary<string, MyEnum> Kvps { get; }
private static IReadOnlyDictionary<string, MyEnum> ToReadOnly(
IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
var filtered = from kvp in kvps
where kvp.Value.HasValue
select kvp;
return new ReadOnlyDictionary<string, MyEnum>(
filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
}
}
The constructor design section of the Framework Design Guidelines suggests that minimal work should be done in constructors so I am opting for the first approach. However, that means every call to MyClass.Kvps will trigger a copy of _kvps which is not ideal.
I would like to know which is a better approach (or are there other ways) in terms of:
Memory efficiency (ideally only one copy of the collection is stored in MyClass)
Performance (property access should be fast and should not trigger a copy of the KeyValuePairs)
Out of the two requirements - don't copy the key value pairs and don't store two copies - you'll have to break one.
What causes us to look at this and think that there must be a solution is that we see TValue and TValue? and our minds want to see them as being of the same type. But they are not the same type.
It becomes clearer if you imagine that instead of TValue and TValue? that these are two different types, like an int and a string, and we want to project a collection of one to a collection of the other while filtering. For example,
List<string> GetStringsFromNonNegativeInts(List<int> ints)
{
return ints.Where(i=>i>-1).Select(i=>i.ToString()).ToList();
}
That's exactly the same scenario as trying to filter a set of TValue? to a set of TValue, even without the dictionary. It's just harder to see. TValue and TValue? code-blind us.
There are only two ways to do this. One is to copy each time, and the other is to keep two lists in synchronization.
EDIT: If you want the latest source values, best way is to implement your own class that implements IReadOnlyDictionary. Initialize this with a private field of ReadOnlyDictionary<string, MyEnum?>. Each call will do the lookup, and if the key exists AND HasValue, return the value.
Note that this implementation depends on the reference to the original values being passed in as an IReadOnlyDictionary to avoid having to copy values over.
public class MyReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TValue : struct
{
// other methods to implement here...
public MyReadOnlyDictionary(IReadOnlyDictionary<TKey, TValue?> kvps)
{
_kvps = kvps;
}
private IReadOnlyDictionary<TKey, TValue?> _kvps;
new public TValue this[TKey key]
{
get
{
TValue? val = _kvps[key];
if (val.HasValue)
return val.Value;
throw new KeyNotFoundException();
}
}
}
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 !
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);
}
}