.net - dictionary.Keys.Add?

2019-02-28 10:50发布

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>?

2条回答
ら.Afraid
2楼-- · 2019-02-28 11:11

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"]);
查看更多
叼着烟拽天下
3楼-- · 2019-02-28 11:26

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.)

查看更多
登录 后发表回答