#if NET20 || NET30 || NET35 || !NET_4_6 using System.Collections.Generic; using LinqInternal.Collections; using LinqInternal.Collections.Specialized; using LinqInternal.Collections.ThreadSafe; namespace System.Collections.Concurrent { [SerializableAttribute] public class ConcurrentDictionary : IDictionary, IDictionary { private readonly ValueCollection _valueCollection; private readonly SafeDictionary _wrapped; public ConcurrentDictionary() : this(4, 31, EqualityComparer.Default) { //Empty } public ConcurrentDictionary(int concurrencyLevel, int capacity) : this(concurrencyLevel, capacity, EqualityComparer.Default) { //Empty } public ConcurrentDictionary(IEnumerable> collection) : this(4, 31, EqualityComparer.Default) { if (ReferenceEquals(collection, null)) { throw new ArgumentNullException("collection"); } AddRange(collection); } public ConcurrentDictionary(IEqualityComparer comparer) : this(4, 31, comparer) { //Empty } public ConcurrentDictionary(IEnumerable> collection, IEqualityComparer comparer) : this(4, 31, comparer) { if (ReferenceEquals(collection, null)) { throw new ArgumentNullException("collection"); } AddRange(collection); } public ConcurrentDictionary(int concurrencyLevel, IEnumerable> collection, IEqualityComparer comparer) : this(concurrencyLevel, 31, comparer) { if (ReferenceEquals(collection, null)) { throw new ArgumentNullException("collection"); } AddRange(collection); } public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer comparer) { if (ReferenceEquals(comparer, null)) { throw new ArgumentNullException("comparer"); } if (concurrencyLevel < 1) { throw new ArgumentOutOfRangeException("concurrencyLevel", "concurrencyLevel < 1"); } if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", "capacity < 0"); } _wrapped = new SafeDictionary(); _valueCollection = new ValueCollection(_wrapped); } public int Count { get { // This should be an snaptshot operation return _wrapped.Count; } } public bool IsEmpty { get { return Count == 0; } } bool IDictionary.IsFixedSize { get { return false; } } bool ICollection>.IsReadOnly { get { return false; } } bool IDictionary.IsReadOnly { get { return false; } } bool ICollection.IsSynchronized { get { return false; } } public ICollection Keys { get { return _wrapped.Keys; } } ICollection IDictionary.Keys { get { return (ICollection)_wrapped.Keys; } } object ICollection.SyncRoot { get { return this; } } public ICollection Values { get { return _valueCollection; } } ICollection IDictionary.Values { get { return _valueCollection; } } public TValue this[TKey key] { get { // key can be null if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped[key]; } set { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } _wrapped.Set(key, value); } } object IDictionary.this[object key] { get { if (key == null) { throw new ArgumentNullException("key"); } // keep the is operator if (key is TKey) { TValue result; if (_wrapped.TryGetValue((TKey)key, out result)) { return result; } } return null; } set { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // keep the is operator if (key is TKey && value is TValue) { this[(TKey)key] = (TValue)value; } throw new ArgumentException(); } } void IDictionary.Add(TKey key, TValue value) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } _wrapped.AddNew(key, value); } void IDictionary.Add(object key, object value) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // keep the is operator if (key is TKey && value is TValue) { _wrapped.AddNew((TKey)key, (TValue)value); } throw new ArgumentException(); } void ICollection>.Add(KeyValuePair item) { if (ReferenceEquals(item.Key, null)) { // ConcurrentDictionary hates null // While technically item is not null and item.Key is not an argument... // This is what happens when you do the call on Microsoft's implementation throw CreateArgumentNullExceptionKey(item.Key); } _wrapped.AddNew(item.Key, item.Value); } public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // addValueFactory and updateValueFactory are checked for null inside the call var result = _wrapped.AddOrUpdate ( key, addValueFactory, updateValueFactory ); return result; } public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // updateValueFactory is checked for null inside the call var result = _wrapped.AddOrUpdate ( key, addValue, updateValueFactory ); return result; } public void Clear() { // This should be an snaptshot operation _wrapped.Clear(); } bool IDictionary.Contains(object key) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // keep the is operator if (key is TKey) { return ContainsKey((TKey)key); } return false; } bool ICollection>.Contains(KeyValuePair item) { if (ReferenceEquals(item.Key, null)) { // ConcurrentDictionary hates null // While technically item is not null and item.Key is not an argument... // This is what happens when you do the call on Microsoft's implementation throw CreateArgumentNullExceptionKey(item.Key); } TValue found; if (_wrapped.TryGetValue(item.Key, out found)) { if (EqualityComparer.Default.Equals(found, item.Value)) { return true; } } return false; } public bool ContainsKey(TKey key) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // No existing value is set, so no locking, right? return _wrapped.ContainsKey(key); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { // This should be an snaptshot operation Extensions.CanCopyTo(_wrapped.Count, array, arrayIndex); this.CopyTo(array, arrayIndex); } void ICollection.CopyTo(Array array, int index) { // WORST API EVER - I shouldn't be supporting this // I'm checking size before checking type - I have no plans to fix that Extensions.CanCopyTo(_wrapped.Count, array, index); try { var pairs = array as KeyValuePair[]; // most decent alternative if (pairs != null) { var keyValuePairs = pairs; foreach (var pair in _wrapped) { keyValuePairs[index] = pair; index++; } return; } var objects = array as object[]; if (objects != null) { var valuePairs = objects; foreach (var pair in _wrapped) { valuePairs[index] = pair; index++; } return; } var entries = array as DictionaryEntry[]; // that thing exists, I was totally unaware, I may as well use it. if (entries != null) { var dictionaryEntries = entries; foreach (var pair in _wrapped) { dictionaryEntries[index] = new DictionaryEntry(pair.Key, pair.Value); index++; } return; } throw new ArgumentException("Not supported array type"); // A.K.A ScrewYouException } catch (IndexOutOfRangeException exception) { throw new ArgumentException("array", exception.Message); } } public IEnumerator> GetEnumerator() { return _wrapped.GetEnumerator(); } IDictionaryEnumerator IDictionary.GetEnumerator() { return new DictionaryEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public TValue GetOrAdd(TKey key, Func valueFactory) { // No existing value is set, so no locking, right? if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // valueFactory is checked for null inside the call return _wrapped.GetOrAdd(key, valueFactory); } public TValue GetOrAdd(TKey key, TValue value) { // No existing value is set, so no locking, right? if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped.GetOrAdd(key, value); } void IDictionary.Remove(object key) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } // keep the is operator if (key is TKey) { _wrapped.Remove((TKey)key); } } bool ICollection>.Remove(KeyValuePair item) { if (ReferenceEquals(item.Key, null)) { // ConcurrentDictionary hates null // While technically item is not null and item.Key is not an argument... // This is what happens when you do the call on Microsoft's implementation throw CreateArgumentNullExceptionKey(item.Key); } TValue found; return _wrapped.Remove(item.Key, input => EqualityComparer.Default.Equals(item.Value, item.Value), out found); } bool IDictionary.Remove(TKey key) { TValue bundle; return TryRemove(key, out bundle); } public KeyValuePair[] ToArray() { // This should be an snaptshot operation var result = new List>(_wrapped.Count); foreach (var pair in _wrapped) { result.Add(pair); } return result.ToArray(); } public bool TryAdd(TKey key, TValue value) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped.TryAdd(key, value); } public bool TryGetValue(TKey key, out TValue value) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped.TryGetValue(key, out value); } public bool TryRemove(TKey key, out TValue value) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped.Remove(key, out value); } public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { if (ReferenceEquals(key, null)) { // ConcurrentDictionary hates null throw new ArgumentNullException("key"); } return _wrapped.TryUpdate(key, newValue, comparisonValue); } private static ArgumentNullException CreateArgumentNullExceptionKey(TKey key) { GC.KeepAlive(key); return new ArgumentNullException("key"); } private void AddRange(IEnumerable> collection) { foreach (var pair in collection) { if (!_wrapped.TryAdd(pair.Key, pair.Value)) { throw new ArgumentException("The source contains duplicate keys."); } } } private sealed class DictionaryEnumerator : IDictionaryEnumerator { private readonly IEnumerator> _wrapped; internal DictionaryEnumerator(ConcurrentDictionary wrapped) { _wrapped = wrapped.GetEnumerator(); } public object Current { get { return Entry; } } public DictionaryEntry Entry { get { var current = _wrapped.Current; return new DictionaryEntry(current.Key, current.Value); } } public object Key { get { var current = _wrapped.Current; return current.Key; } } public object Value { get { var current = _wrapped.Current; return current.Value; } } public bool MoveNext() { if (_wrapped.MoveNext()) { return true; } // The DictionaryEnumerator of ConcurrentDictionary is not IDisposable... // Which means this method takes the responsability of disposing _wrapped.Dispose(); return false; } public void Reset() { // This is two levels deep of you should not be using this. // You should not be using DictionaryEnumerator // And you should not be calling Reset throw new NotSupportedException(); } } } } #endif