网上演练
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

581 lines
18 KiB

#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<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
{
private readonly ValueCollection<TKey, TValue> _valueCollection;
private readonly SafeDictionary<TKey, TValue> _wrapped;
public ConcurrentDictionary()
: this(4, 31, EqualityComparer<TKey>.Default)
{
//Empty
}
public ConcurrentDictionary(int concurrencyLevel, int capacity)
: this(concurrencyLevel, capacity, EqualityComparer<TKey>.Default)
{
//Empty
}
public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
: this(4, 31, EqualityComparer<TKey>.Default)
{
if (ReferenceEquals(collection, null))
{
throw new ArgumentNullException("collection");
}
AddRange(collection);
}
public ConcurrentDictionary(IEqualityComparer<TKey> comparer)
: this(4, 31, comparer)
{
//Empty
}
public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
: this(4, 31, comparer)
{
if (ReferenceEquals(collection, null))
{
throw new ArgumentNullException("collection");
}
AddRange(collection);
}
public ConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
: this(concurrencyLevel, 31, comparer)
{
if (ReferenceEquals(collection, null))
{
throw new ArgumentNullException("collection");
}
AddRange(collection);
}
public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> 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<TKey, TValue>();
_valueCollection = new ValueCollection<TKey, TValue>(_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<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get { return false; }
}
bool IDictionary.IsReadOnly
{
get { return false; }
}
bool ICollection.IsSynchronized
{
get { return false; }
}
public ICollection<TKey> Keys
{
get { return _wrapped.Keys; }
}
ICollection IDictionary.Keys
{
get { return (ICollection)_wrapped.Keys; }
}
object ICollection.SyncRoot
{
get { return this; }
}
public ICollection<TValue> 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<TKey, TValue>.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<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> 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<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> 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<TKey, TValue, TValue> 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<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> 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<TValue>.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<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] 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<TKey, TValue>[]; // 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<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _wrapped.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return new DictionaryEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public TValue GetOrAdd(TKey key, Func<TKey, TValue> 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<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> 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<TValue>.Default.Equals(item.Value, item.Value), out found);
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
TValue bundle;
return TryRemove(key, out bundle);
}
public KeyValuePair<TKey, TValue>[] ToArray()
{
// This should be an snaptshot operation
var result = new List<KeyValuePair<TKey, TValue>>(_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<KeyValuePair<TKey, TValue>> 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<KeyValuePair<TKey, TValue>> _wrapped;
internal DictionaryEnumerator(ConcurrentDictionary<TKey, TValue> 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