In .Net, IDictionary<K, V>
defines .Keys
and .Values
properties, each of which is an ICollection<>
rather than IEnumerable<>
, which seems like it would be a more natural fit to me.
Is there any reasonable use case to call .Add
or .Remove
on .Keys
or .Values
of an instance of an IDictionary<K, V>
?
No, probably no reasonable use case. There are very few (probably zero) legitimate reasons for this at all.
The Dictionary<TKey, TValue>
class returns a KeyCollection
for its .Keys
which in turn throws NotSupportedException
with "Mutating a key collection derived from a dictionary is not allowed." whenever trying to add directly to it. I imagine that it returns an ICollection
for legacy reasons and probably should be avoided at all costs now.
In fact unless the ICollection
returned from .Keys
had a reference to its containing IDictionary
, I can't see anything useful happen. The .Add
of the ICollection
would have to tell the containing IDictionary
what this add meant. Perhaps you wanted to implement some form of a Set
you could do something like this:
public class StringSet : IDictionary<string, int> {
private readonly Dictionary<string, int> _InternalDictionary = new Dictionary<string, int>();
public int this[string key] {
get { return _InternalDictionary[key]; }
set { _InternalDictionary[key] = value; }
}
private StringCollection _Keys;
public ICollection<string> Keys {
get {
if(_Keys == null) _Keys = new StringCollection(this);
return _Keys;
}
}
ICollection<string> IDictionary<string, int>.Keys {
get {
if(_Keys == null) _Keys = new StringCollection(this);
return _Keys;
}
}
public ICollection<int> Values { get { throw new NotImplementedException();} }
public void Add(string key, int value) { _InternalDictionary.Add(key, value); }
public bool ContainsKey(string key) { return _InternalDictionary.ContainsKey(key); }
public bool Remove(string key) { return _InternalDictionary.Remove(key); }
public bool TryGetValue(string key, out int value) { return _InternalDictionary.TryGetValue(key, out value); }
public void Clear() { throw new NotImplementedException(); }
public void Add(KeyValuePair<string, int> item) { throw new NotImplementedException(); }
public bool Contains(KeyValuePair<string, int> item) { throw new NotImplementedException(); }
public void CopyTo(KeyValuePair<string, int>[] array, int arrayIndex) { throw new NotImplementedException(); }
public bool Remove(KeyValuePair<string, int> item) { throw new NotImplementedException(); }
public int Count { get { return _InternalDictionary.Count; } }
public bool IsReadOnly { get { return false; } }
public IEnumerator<KeyValuePair<string, int>> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public class StringCollection : ICollection<string> {
private readonly StringSet _ContainingSet;
public StringCollection(StringSet set) {
_ContainingSet = set;
}
public void Add(string item) {
if(_ContainingSet.ContainsKey(item)) _ContainingSet[item]++;
else _ContainingSet[item] = 1;
}
public bool Contains(string item) { return _ContainingSet.ContainsKey(item); }
public bool Remove(string item) { throw new NotImplementedException(); }
public void Clear() { throw new NotImplementedException(); }
public void CopyTo(string[] array, int arrayIndex) { throw new NotImplementedException(); }
public int Count { get { return _ContainingSet.Count; } }
public bool IsReadOnly { get { return false; } }
public IEnumerator<string> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
Surely there is a better way to implement this or similar for your specific needs. The only benefit this has is allowing .Add
on the returned Keys
StringCollection
. I would want to force people using my StringSet
to use the parent StringSet
class anyway. But, it is possible that someone would want the above overridden behavior.
Call it with this:
var set = new StringSet();
var keys = set.Keys;
keys.Add("hello");
keys.Add("hello");
keys.Add("world");
Debug.Print("hello: {0}, world: {1}", set["hello"], set["world"]);
I can't think of any reasonable use case for calling Add
or Remove
on the key or value collections. What would you expect the resulting dictionary to look like?
I'm pretty sure that all of the framework's built-in implementations of IDictionary<K,V>
will throw a NotSupportedException
, or similar, if you try to do it. (And you should probably do the same thing in any of your own implementations too.)