| | |
| | | namespace TcpServer |
| | | { |
| | | |
| | | /// <summary> |
| | | /// Represents a thread-safe collection of keys and values. |
| | | /// </summary> |
| | | /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> |
| | | /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> |
| | | /// <remarks> |
| | | /// All public and protected members of <see cref="ConcurrentDictionary{TKey,TValue}"/> are thread-safe and may be used |
| | | /// concurrently from multiple threads. |
| | | /// </remarks> |
| | | [Serializable] |
| | | [ComVisible (false)] |
| | | [DebuggerDisplay ("Count = {Count}")] |
| | | // [HostProtection(Synchronization = true, ExternalThreading = true)] |
| | | /// <summary> |
| | | /// Represents a thread-safe collection of keys and values. |
| | | /// </summary> |
| | | /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> |
| | | /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> |
| | | /// <remarks> |
| | | /// All public and protected members of <see cref="ConcurrentDictionary{TKey,TValue}"/> are thread-safe and may be used |
| | | /// concurrently from multiple threads. |
| | | /// </remarks> |
| | | [Serializable] |
| | | [ComVisible(false)] |
| | | [DebuggerDisplay("Count = {Count}")] |
| | | // [HostProtection(Synchronization = true, ExternalThreading = true)] |
| | | public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary |
| | | { |
| | | [NonSerialized] |
| | | private volatile Node[] m_buckets; |
| | | // A singly-linked list for each bucket. |
| | | [NonSerialized] |
| | | private object[] m_locks; |
| | | // A set of locks, each guarding a section of the table. |
| | | [NonSerialized] |
| | | private volatile int[] m_countPerLock; |
| | | // The number of elements guarded by each lock. |
| | | private IEqualityComparer<TKey> m_comparer; |
| | | // Key equality comparer |
| | | { |
| | | [NonSerialized] |
| | | private volatile Node[] m_buckets; |
| | | // A singly-linked list for each bucket. |
| | | [NonSerialized] |
| | | private object[] m_locks; |
| | | // A set of locks, each guarding a section of the table. |
| | | [NonSerialized] |
| | | private volatile int[] m_countPerLock; |
| | | // The number of elements guarded by each lock. |
| | | private IEqualityComparer<TKey> m_comparer; |
| | | // Key equality comparer |
| | | |
| | | private KeyValuePair<TKey, TValue>[] m_serializationArray; |
| | | // Used for custom serialization |
| | | private KeyValuePair<TKey, TValue>[] m_serializationArray; |
| | | // Used for custom serialization |
| | | |
| | | private int m_serializationConcurrencyLevel; |
| | | // used to save the concurrency level in serialization |
| | | private int m_serializationConcurrencyLevel; |
| | | // used to save the concurrency level in serialization |
| | | |
| | | private int m_serializationCapacity; |
| | | // used to save the capacity in serialization |
| | | private int m_serializationCapacity; |
| | | // used to save the capacity in serialization |
| | | |
| | | // The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the |
| | | // DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference |
| | | // and blocking, but also the more expensive operations that require all locks become (e.g. table |
| | | // resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good |
| | | // compromise. |
| | | private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4; |
| | | // The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the |
| | | // DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference |
| | | // and blocking, but also the more expensive operations that require all locks become (e.g. table |
| | | // resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good |
| | | // compromise. |
| | | private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4; |
| | | |
| | | // The default capacity, i.e. the initial # of buckets. When choosing this value, we are making |
| | | // a trade-off between the size of a very small dictionary, and the number of resizes when |
| | | // constructing a large dictionary. Also, the capacity should not be divisible by a small prime. |
| | | private const int DEFAULT_CAPACITY = 31; |
| | | // The default capacity, i.e. the initial # of buckets. When choosing this value, we are making |
| | | // a trade-off between the size of a very small dictionary, and the number of resizes when |
| | | // constructing a large dictionary. Also, the capacity should not be divisible by a small prime. |
| | | private const int DEFAULT_CAPACITY = 31; |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the default concurrency level, has the default initial capacity, and |
| | | /// uses the default comparer for the key type. |
| | | /// </summary> |
| | | public ConcurrentDictionary () : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY) |
| | | { |
| | | } |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the default concurrency level, has the default initial capacity, and |
| | | /// uses the default comparer for the key type. |
| | | /// </summary> |
| | | public ConcurrentDictionary() : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY) |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level and capacity, and uses the default |
| | | /// comparer for the key type. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="capacity">The initial number of elements that the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// can contain.</param> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is |
| | | /// less than 1.</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than |
| | | /// 0.</exception> |
| | | public ConcurrentDictionary (int concurrencyLevel, int capacity) : this (concurrencyLevel, capacity, EqualityComparer<TKey>.Default) |
| | | { |
| | | } |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level and capacity, and uses the default |
| | | /// comparer for the key type. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="capacity">The initial number of elements that the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// can contain.</param> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is |
| | | /// less than 1.</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than |
| | | /// 0.</exception> |
| | | public ConcurrentDictionary(int concurrencyLevel, int capacity) : this(concurrencyLevel, capacity, EqualityComparer<TKey>.Default) |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/>, has the default concurrency |
| | | /// level, has the default initial capacity, and uses the default comparer for the key type. |
| | | /// </summary> |
| | | /// <param name="collection">The <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to |
| | | /// the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more |
| | | /// duplicate keys.</exception> |
| | | public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> collection) : this (collection, EqualityComparer<TKey>.Default) |
| | | { |
| | | } |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/>, has the default concurrency |
| | | /// level, has the default initial capacity, and uses the default comparer for the key type. |
| | | /// </summary> |
| | | /// <param name="collection">The <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to |
| | | /// the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more |
| | | /// duplicate keys.</exception> |
| | | public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, EqualityComparer<TKey>.Default) |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level and capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public ConcurrentDictionary (IEqualityComparer<TKey> comparer) : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY, comparer) |
| | | { |
| | | } |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level and capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public ConcurrentDictionary(IEqualityComparer<TKey> comparer) : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, comparer) |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see |
| | | /// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default |
| | | /// initial capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="collection">The <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to |
| | | /// the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference |
| | | /// (Nothing in Visual Basic). -or- |
| | | /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). |
| | | /// </exception> |
| | | public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) |
| | | : this (DefaultConcurrencyLevel, collection, comparer) |
| | | { |
| | | } |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see |
| | | /// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default |
| | | /// initial capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="collection">The <see |
| | | /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to |
| | | /// the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference |
| | | /// (Nothing in Visual Basic). -or- |
| | | /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). |
| | | /// </exception> |
| | | public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) |
| | | : this(DefaultConcurrencyLevel, collection, comparer) |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>, |
| | | /// has the specified concurrency level, has the specified initial capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="collection">The <see cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> implementation to use |
| | | /// when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"> |
| | | /// <paramref name="collection"/> is a null reference (Nothing in Visual Basic). |
| | | /// -or- |
| | | /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> |
| | | /// <paramref name="concurrencyLevel"/> is less than 1. |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more duplicate keys.</exception> |
| | | public ConcurrentDictionary ( |
| | | int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) |
| | | : this (concurrencyLevel, DEFAULT_CAPACITY, comparer) |
| | | { |
| | | if (collection == null) |
| | | throw new ArgumentNullException ("collection"); |
| | | if (comparer == null) |
| | | throw new ArgumentNullException ("comparer"); |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>, |
| | | /// has the specified concurrency level, has the specified initial capacity, and uses the specified |
| | | /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="collection">The <see cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to the new |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> implementation to use |
| | | /// when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"> |
| | | /// <paramref name="collection"/> is a null reference (Nothing in Visual Basic). |
| | | /// -or- |
| | | /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> |
| | | /// <paramref name="concurrencyLevel"/> is less than 1. |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more duplicate keys.</exception> |
| | | public ConcurrentDictionary( |
| | | int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) |
| | | : this(concurrencyLevel, DEFAULT_CAPACITY, comparer) |
| | | { |
| | | if (collection == null) |
| | | throw new ArgumentNullException("collection"); |
| | | if (comparer == null) |
| | | throw new ArgumentNullException("comparer"); |
| | | |
| | | InitializeFromCollection (collection); |
| | | } |
| | | InitializeFromCollection(collection); |
| | | } |
| | | |
| | | private void InitializeFromCollection (IEnumerable<KeyValuePair<TKey, TValue>> collection) |
| | | { |
| | | TValue dummy; |
| | | foreach (KeyValuePair<TKey, TValue> pair in collection) { |
| | | if (pair.Key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | private void InitializeFromCollection(IEnumerable<KeyValuePair<TKey, TValue>> collection) |
| | | { |
| | | TValue dummy; |
| | | foreach (KeyValuePair<TKey, TValue> pair in collection) |
| | | { |
| | | if (pair.Key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | if (!TryAddInternal (pair.Key, pair.Value, false, false, out dummy)) { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_SourceContainsDuplicateKeys")); |
| | | } |
| | | } |
| | | } |
| | | if (!TryAddInternal(pair.Key, pair.Value, false, false, out dummy)) |
| | | { |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_SourceContainsDuplicateKeys")); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level, has the specified initial capacity, and |
| | | /// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="capacity">The initial number of elements that the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// can contain.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> |
| | | /// <paramref name="concurrencyLevel"/> is less than 1. -or- |
| | | /// <paramref name="capacity"/> is less than 0. |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) |
| | | { |
| | | if (concurrencyLevel < 1) { |
| | | throw new ArgumentOutOfRangeException ("concurrencyLevel", GetResource ("ConcurrentDictionary_ConcurrencyLevelMustBePositive")); |
| | | } |
| | | if (capacity < 0) { |
| | | throw new ArgumentOutOfRangeException ("capacity", GetResource ("ConcurrentDictionary_CapacityMustNotBeNegative")); |
| | | } |
| | | if (comparer == null) |
| | | throw new ArgumentNullException ("comparer"); |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// class that is empty, has the specified concurrency level, has the specified initial capacity, and |
| | | /// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. |
| | | /// </summary> |
| | | /// <param name="concurrencyLevel">The estimated number of threads that will update the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> |
| | | /// <param name="capacity">The initial number of elements that the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// can contain.</param> |
| | | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> |
| | | /// implementation to use when comparing keys.</param> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"> |
| | | /// <paramref name="concurrencyLevel"/> is less than 1. -or- |
| | | /// <paramref name="capacity"/> is less than 0. |
| | | /// </exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) |
| | | { |
| | | if (concurrencyLevel < 1) |
| | | { |
| | | throw new ArgumentOutOfRangeException("concurrencyLevel", GetResource("ConcurrentDictionary_ConcurrencyLevelMustBePositive")); |
| | | } |
| | | if (capacity < 0) |
| | | { |
| | | throw new ArgumentOutOfRangeException("capacity", GetResource("ConcurrentDictionary_CapacityMustNotBeNegative")); |
| | | } |
| | | if (comparer == null) |
| | | throw new ArgumentNullException("comparer"); |
| | | |
| | | // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard |
| | | // any buckets. |
| | | if (capacity < concurrencyLevel) { |
| | | capacity = concurrencyLevel; |
| | | } |
| | | // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard |
| | | // any buckets. |
| | | if (capacity < concurrencyLevel) |
| | | { |
| | | capacity = concurrencyLevel; |
| | | } |
| | | |
| | | m_locks = new object[concurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | m_locks [i] = new object (); |
| | | } |
| | | m_locks = new object[concurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | m_locks[i] = new object(); |
| | | } |
| | | |
| | | m_countPerLock = new int[m_locks.Length]; |
| | | m_buckets = new Node[capacity]; |
| | | m_comparer = comparer; |
| | | } |
| | | m_countPerLock = new int[m_locks.Length]; |
| | | m_buckets = new Node[capacity]; |
| | | m_comparer = comparer; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// Attempts to add the specified key and value to the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="value">The value of the element to add. The value can be a null reference (Nothing |
| | | /// in Visual Basic) for reference types.</param> |
| | | /// <returns>true if the key/value pair was added to the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/> |
| | | /// successfully; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The <see cref="ConcurrentDictionary{TKey, TValue}"/> |
| | | /// contains too many elements.</exception> |
| | | public bool TryAdd (TKey key, TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | TValue dummy; |
| | | return TryAddInternal (key, value, false, true, out dummy); |
| | | } |
| | | /// <summary> |
| | | /// Attempts to add the specified key and value to the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="value">The value of the element to add. The value can be a null reference (Nothing |
| | | /// in Visual Basic) for reference types.</param> |
| | | /// <returns>true if the key/value pair was added to the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/> |
| | | /// successfully; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The <see cref="ConcurrentDictionary{TKey, TValue}"/> |
| | | /// contains too many elements.</exception> |
| | | public bool TryAdd(TKey key, TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | TValue dummy; |
| | | return TryAddInternal(key, value, false, true, out dummy); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Determines whether the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains the specified |
| | | /// key. |
| | | /// </summary> |
| | | /// <param name="key">The key to locate in the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/>.</param> |
| | | /// <returns>true if the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains an element with |
| | | /// the specified key; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool ContainsKey (TKey key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Determines whether the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains the specified |
| | | /// key. |
| | | /// </summary> |
| | | /// <param name="key">The key to locate in the <see cref="ConcurrentDictionary{TKey, |
| | | /// TValue}"/>.</param> |
| | | /// <returns>true if the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains an element with |
| | | /// the specified key; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool ContainsKey(TKey key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | TValue throwAwayValue; |
| | | return TryGetValue (key, out throwAwayValue); |
| | | } |
| | | TValue throwAwayValue; |
| | | return TryGetValue(key, out throwAwayValue); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Attempts to remove and return the the value with the specified key from the |
| | | /// <see cref="ConcurrentDictionary{TKey, TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove and return.</param> |
| | | /// <param name="value">When this method returns, <paramref name="value"/> contains the object removed from the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> or the default value of <typeparamref |
| | | /// name="TValue"/> |
| | | /// if the operation failed.</param> |
| | | /// <returns>true if an object was removed successfully; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool TryRemove (TKey key, out TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Attempts to remove and return the the value with the specified key from the |
| | | /// <see cref="ConcurrentDictionary{TKey, TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove and return.</param> |
| | | /// <param name="value">When this method returns, <paramref name="value"/> contains the object removed from the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> or the default value of <typeparamref |
| | | /// name="TValue"/> |
| | | /// if the operation failed.</param> |
| | | /// <returns>true if an object was removed successfully; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool TryRemove(TKey key, out TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | return TryRemoveInternal (key, out value, false, default(TValue)); |
| | | } |
| | | return TryRemoveInternal(key, out value, false, default(TValue)); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Removes the specified key from the dictionary if it exists and returns its associated value. |
| | | /// If matchValue flag is set, the key will be removed only if is associated with a particular |
| | | /// value. |
| | | /// </summary> |
| | | /// <param name="key">The key to search for and remove if it exists.</param> |
| | | /// <param name="value">The variable into which the removed value, if found, is stored.</param> |
| | | /// <param name="matchValue">Whether removal of the key is conditional on its value.</param> |
| | | /// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param> |
| | | /// <returns></returns> |
| | | private bool TryRemoveInternal (TKey key, out TValue value, bool matchValue, TValue oldValue) |
| | | { |
| | | while (true) { |
| | | Node[] buckets = m_buckets; |
| | | /// <summary> |
| | | /// Removes the specified key from the dictionary if it exists and returns its associated value. |
| | | /// If matchValue flag is set, the key will be removed only if is associated with a particular |
| | | /// value. |
| | | /// </summary> |
| | | /// <param name="key">The key to search for and remove if it exists.</param> |
| | | /// <param name="value">The variable into which the removed value, if found, is stored.</param> |
| | | /// <param name="matchValue">Whether removal of the key is conditional on its value.</param> |
| | | /// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param> |
| | | /// <returns></returns> |
| | | private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue) |
| | | { |
| | | while (true) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | |
| | | int bucketNo, lockNo; |
| | | GetBucketAndLockNo (m_comparer.GetHashCode (key), out bucketNo, out lockNo, buckets.Length); |
| | | int bucketNo, lockNo; |
| | | GetBucketAndLockNo(m_comparer.GetHashCode(key), out bucketNo, out lockNo, buckets.Length); |
| | | |
| | | lock (m_locks[lockNo]) { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | continue; |
| | | } |
| | | lock (m_locks[lockNo]) |
| | | { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | Node prev = null; |
| | | for (Node curr = m_buckets [bucketNo]; curr != null; curr = curr.m_next) { |
| | | Assert ((prev == null && curr == m_buckets [bucketNo]) || prev.m_next == curr); |
| | | Node prev = null; |
| | | for (Node curr = m_buckets[bucketNo]; curr != null; curr = curr.m_next) |
| | | { |
| | | Assert((prev == null && curr == m_buckets[bucketNo]) || prev.m_next == curr); |
| | | |
| | | if (m_comparer.Equals (curr.m_key, key)) { |
| | | if (matchValue) { |
| | | bool valuesMatch = EqualityComparer<TValue>.Default.Equals (oldValue, curr.m_value); |
| | | if (!valuesMatch) { |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | } |
| | | if (m_comparer.Equals(curr.m_key, key)) |
| | | { |
| | | if (matchValue) |
| | | { |
| | | bool valuesMatch = EqualityComparer<TValue>.Default.Equals(oldValue, curr.m_value); |
| | | if (!valuesMatch) |
| | | { |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | if (prev == null) { |
| | | m_buckets [bucketNo] = curr.m_next; |
| | | } else { |
| | | prev.m_next = curr.m_next; |
| | | } |
| | | if (prev == null) |
| | | { |
| | | m_buckets[bucketNo] = curr.m_next; |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = curr.m_next; |
| | | } |
| | | |
| | | value = curr.m_value; |
| | | m_countPerLock [lockNo]--; |
| | | return true; |
| | | } |
| | | prev = curr; |
| | | } |
| | | } |
| | | value = curr.m_value; |
| | | m_countPerLock[lockNo]--; |
| | | return true; |
| | | } |
| | | prev = curr; |
| | | } |
| | | } |
| | | |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | } |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Attempts to get the value associated with the specified key from the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get.</param> |
| | | /// <param name="value">When this method returns, <paramref name="value"/> contains the object from |
| | | /// the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> with the spedified key or the default value of |
| | | /// <typeparamref name="TValue"/>, if the operation failed.</param> |
| | | /// <returns>true if the key was found in the <see cref="ConcurrentDictionary{TKey,TValue}"/>; |
| | | /// otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool TryGetValue (TKey key, out TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Attempts to get the value associated with the specified key from the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get.</param> |
| | | /// <param name="value">When this method returns, <paramref name="value"/> contains the object from |
| | | /// the |
| | | /// <see cref="ConcurrentDictionary{TKey,TValue}"/> with the spedified key or the default value of |
| | | /// <typeparamref name="TValue"/>, if the operation failed.</param> |
| | | /// <returns>true if the key was found in the <see cref="ConcurrentDictionary{TKey,TValue}"/>; |
| | | /// otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public bool TryGetValue(TKey key, out TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | int bucketNo, lockNoUnused; |
| | | int bucketNo, lockNoUnused; |
| | | |
| | | // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize. |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo (m_comparer.GetHashCode (key), out bucketNo, out lockNoUnused, buckets.Length); |
| | | // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize. |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo(m_comparer.GetHashCode(key), out bucketNo, out lockNoUnused, buckets.Length); |
| | | |
| | | // We can get away w/out a lock here. |
| | | Node n = buckets [bucketNo]; |
| | | // We can get away w/out a lock here. |
| | | Node n = buckets[bucketNo]; |
| | | |
| | | // The memory barrier ensures that the load of the fields of 'n' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier (); |
| | | while (n != null) { |
| | | if (m_comparer.Equals (n.m_key, key)) { |
| | | value = n.m_value; |
| | | return true; |
| | | } |
| | | n = n.m_next; |
| | | } |
| | | // The memory barrier ensures that the load of the fields of 'n' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier(); |
| | | while (n != null) |
| | | { |
| | | if (m_comparer.Equals(n.m_key, key)) |
| | | { |
| | | value = n.m_value; |
| | | return true; |
| | | } |
| | | n = n.m_next; |
| | | } |
| | | |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Compares the existing value for the specified key with a specified value, and if they’re equal, |
| | | /// updates the key with a third value. |
| | | /// </summary> |
| | | /// <param name="key">The key whose value is compared with <paramref name="comparisonValue"/> and |
| | | /// possibly replaced.</param> |
| | | /// <param name="newValue">The value that replaces the value of the element with <paramref |
| | | /// name="key"/> if the comparison results in equality.</param> |
| | | /// <param name="comparisonValue">The value that is compared to the value of the element with |
| | | /// <paramref name="key"/>.</param> |
| | | /// <returns>true if the value with <paramref name="key"/> was equal to <paramref |
| | | /// name="comparisonValue"/> and replaced with <paramref name="newValue"/>; otherwise, |
| | | /// false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null |
| | | /// reference.</exception> |
| | | public bool TryUpdate (TKey key, TValue newValue, TValue comparisonValue) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Compares the existing value for the specified key with a specified value, and if they’re equal, |
| | | /// updates the key with a third value. |
| | | /// </summary> |
| | | /// <param name="key">The key whose value is compared with <paramref name="comparisonValue"/> and |
| | | /// possibly replaced.</param> |
| | | /// <param name="newValue">The value that replaces the value of the element with <paramref |
| | | /// name="key"/> if the comparison results in equality.</param> |
| | | /// <param name="comparisonValue">The value that is compared to the value of the element with |
| | | /// <paramref name="key"/>.</param> |
| | | /// <returns>true if the value with <paramref name="key"/> was equal to <paramref |
| | | /// name="comparisonValue"/> and replaced with <paramref name="newValue"/>; otherwise, |
| | | /// false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null |
| | | /// reference.</exception> |
| | | public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | int hashcode = m_comparer.GetHashCode (key); |
| | | IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default; |
| | | int hashcode = m_comparer.GetHashCode(key); |
| | | IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default; |
| | | |
| | | while (true) { |
| | | int bucketNo; |
| | | int lockNo; |
| | | while (true) |
| | | { |
| | | int bucketNo; |
| | | int lockNo; |
| | | |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo (hashcode, out bucketNo, out lockNo, buckets.Length); |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, buckets.Length); |
| | | |
| | | lock (m_locks[lockNo]) { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | continue; |
| | | } |
| | | lock (m_locks[lockNo]) |
| | | { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets [bucketNo]; node != null; node = node.m_next) { |
| | | Assert ((prev == null && node == m_buckets [bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals (node.m_key, key)) { |
| | | if (valueComparer.Equals (node.m_value, comparisonValue)) { |
| | | // Replace the old node with a new node. Unfortunately, we cannot simply |
| | | // change node.m_value in place. We don't know the size of TValue, so |
| | | // its writes may not be atomic. |
| | | Node newNode = new Node (node.m_key, newValue, hashcode, node.m_next); |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets[bucketNo]; node != null; node = node.m_next) |
| | | { |
| | | Assert((prev == null && node == m_buckets[bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals(node.m_key, key)) |
| | | { |
| | | if (valueComparer.Equals(node.m_value, comparisonValue)) |
| | | { |
| | | // Replace the old node with a new node. Unfortunately, we cannot simply |
| | | // change node.m_value in place. We don't know the size of TValue, so |
| | | // its writes may not be atomic. |
| | | Node newNode = new Node(node.m_key, newValue, hashcode, node.m_next); |
| | | |
| | | if (prev == null) { |
| | | buckets [bucketNo] = newNode; |
| | | } else { |
| | | prev.m_next = newNode; |
| | | } |
| | | if (prev == null) |
| | | { |
| | | buckets[bucketNo] = newNode; |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = newNode; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | prev = node; |
| | | } |
| | | prev = node; |
| | | } |
| | | |
| | | //didn't find the key |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | //didn't find the key |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Removes all keys and values from the <see cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | public void Clear () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | /// <summary> |
| | | /// Removes all keys and values from the <see cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | public void Clear() |
| | | { |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | |
| | | m_buckets = new Node[DEFAULT_CAPACITY]; |
| | | Array.Clear (m_countPerLock, 0, m_countPerLock.Length); |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | m_buckets = new Node[DEFAULT_CAPACITY]; |
| | | Array.Clear(m_countPerLock, 0, m_countPerLock.Length); |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection"/> to an array of |
| | | /// type <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>, starting at the |
| | | /// specified array index. |
| | | /// </summary> |
| | | /// <param name="array">The one-dimensional array of type <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// that is the destination of the <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> elements copied from the <see |
| | | /// cref="T:System.Collections.ICollection"/>. The array must have zero-based indexing.</param> |
| | | /// <param name="index">The zero-based index in <paramref name="array"/> at which copying |
| | | /// begins.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than |
| | | /// 0.</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than |
| | | /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see |
| | | /// cref="T:System.Collections.ICollection"/> |
| | | /// is greater than the available space from <paramref name="index"/> to the end of the destination |
| | | /// <paramref name="array"/>.</exception> |
| | | void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int index) |
| | | { |
| | | if (array == null) |
| | | throw new ArgumentNullException ("array"); |
| | | if (index < 0) |
| | | throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative")); |
| | | /// <summary> |
| | | /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection"/> to an array of |
| | | /// type <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>, starting at the |
| | | /// specified array index. |
| | | /// </summary> |
| | | /// <param name="array">The one-dimensional array of type <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// that is the destination of the <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> elements copied from the <see |
| | | /// cref="T:System.Collections.ICollection"/>. The array must have zero-based indexing.</param> |
| | | /// <param name="index">The zero-based index in <paramref name="array"/> at which copying |
| | | /// begins.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than |
| | | /// 0.</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than |
| | | /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see |
| | | /// cref="T:System.Collections.ICollection"/> |
| | | /// is greater than the available space from <paramref name="index"/> to the end of the destination |
| | | /// <paramref name="array"/>.</exception> |
| | | void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index) |
| | | { |
| | | if (array == null) |
| | | throw new ArgumentNullException("array"); |
| | | if (index < 0) |
| | | throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative")); |
| | | |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | |
| | | int count = 0; |
| | | int count = 0; |
| | | |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock[i]; |
| | | } |
| | | |
| | | if (array.Length - count < index || count < 0) { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | if (array.Length - count < index || count < 0) |
| | | { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | |
| | | CopyToPairs (array, index); |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | CopyToPairs(array, index); |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Copies the key and value pairs stored in the <see cref="ConcurrentDictionary{TKey,TValue}"/> to a |
| | | /// new array. |
| | | /// </summary> |
| | | /// <returns>A new array containing a snapshot of key and value pairs copied from the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | public KeyValuePair<TKey, TValue>[] ToArray () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | int count = 0; |
| | | checked { |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Copies the key and value pairs stored in the <see cref="ConcurrentDictionary{TKey,TValue}"/> to a |
| | | /// new array. |
| | | /// </summary> |
| | | /// <returns>A new array containing a snapshot of key and value pairs copied from the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | public KeyValuePair<TKey, TValue>[] ToArray() |
| | | { |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | int count = 0; |
| | | checked |
| | | { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock[i]; |
| | | } |
| | | } |
| | | |
| | | KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count]; |
| | | KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count]; |
| | | |
| | | CopyToPairs (array, 0); |
| | | return array; |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | CopyToPairs(array, 0); |
| | | return array; |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToPairs. |
| | | /// </summary> |
| | | private void CopyToPairs (KeyValuePair<TKey, TValue>[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | array [index] = new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToPairs is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToPairs. |
| | | /// </summary> |
| | | private void CopyToPairs(KeyValuePair<TKey, TValue>[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToPairs is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToEntries. |
| | | /// </summary> |
| | | private void CopyToEntries (DictionaryEntry[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | array [index] = new DictionaryEntry (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToEntries is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToEntries. |
| | | /// </summary> |
| | | private void CopyToEntries(DictionaryEntry[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array[index] = new DictionaryEntry(current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToEntries is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToObjects. |
| | | /// </summary> |
| | | private void CopyToObjects (object[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | array [index] = new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToObjects is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. |
| | | /// |
| | | /// Important: the caller must hold all locks in m_locks before calling CopyToObjects. |
| | | /// </summary> |
| | | private void CopyToObjects(object[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToObjects is only called when there's no overflow risk |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary>Returns an enumerator that iterates through the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | /// <remarks> |
| | | /// The enumerator returned from the dictionary is safe to use concurrently with |
| | | /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot |
| | | /// of the dictionary. The contents exposed through the enumerator may contain modifications |
| | | /// made to the dictionary after <see cref="GetEnumerator"/> was called. |
| | | /// </remarks> |
| | | public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | /// <summary>Returns an enumerator that iterates through the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | /// <remarks> |
| | | /// The enumerator returned from the dictionary is safe to use concurrently with |
| | | /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot |
| | | /// of the dictionary. The contents exposed through the enumerator may contain modifications |
| | | /// made to the dictionary after <see cref="GetEnumerator"/> was called. |
| | | /// </remarks> |
| | | public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | Node current = buckets [i]; |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | Node current = buckets[i]; |
| | | |
| | | // The memory barrier ensures that the load of the fields of 'current' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier (); |
| | | while (current != null) { |
| | | yield return new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | } |
| | | // The memory barrier ensures that the load of the fields of 'current' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier(); |
| | | while (current != null) |
| | | { |
| | | yield return new KeyValuePair<TKey, TValue>(current.m_key, current.m_value); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Shared internal implementation for inserts and updates. |
| | | /// If key exists, we always return false; and if updateIfExists == true we force update with value; |
| | | /// If key doesn't exist, we always add value and return true; |
| | | /// </summary> |
| | | private bool TryAddInternal (TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue) |
| | | { |
| | | int hashcode = m_comparer.GetHashCode (key); |
| | | /// <summary> |
| | | /// Shared internal implementation for inserts and updates. |
| | | /// If key exists, we always return false; and if updateIfExists == true we force update with value; |
| | | /// If key doesn't exist, we always add value and return true; |
| | | /// </summary> |
| | | private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue) |
| | | { |
| | | int hashcode = m_comparer.GetHashCode(key); |
| | | |
| | | while (true) { |
| | | int bucketNo, lockNo; |
| | | while (true) |
| | | { |
| | | int bucketNo, lockNo; |
| | | |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo (hashcode, out bucketNo, out lockNo, buckets.Length); |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, buckets.Length); |
| | | |
| | | bool resizeDesired = false; |
| | | bool lockTaken = false; |
| | | try { |
| | | if (acquireLock) { |
| | | Monitor.Enter (m_locks [lockNo]); |
| | | lockTaken = true; |
| | | } |
| | | bool resizeDesired = false; |
| | | bool lockTaken = false; |
| | | try |
| | | { |
| | | if (acquireLock) |
| | | { |
| | | Monitor.Enter(m_locks[lockNo]); |
| | | lockTaken = true; |
| | | } |
| | | |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | continue; |
| | | } |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets [bucketNo]; node != null; node = node.m_next) { |
| | | Assert ((prev == null && node == m_buckets [bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals (node.m_key, key)) { |
| | | // The key was found in the dictionary. If updates are allowed, update the value for that key. |
| | | // We need to create a new node for the update, in order to support TValue types that cannot |
| | | // be written atomically, since lock-free reads may be happening concurrently. |
| | | if (updateIfExists) { |
| | | Node newNode = new Node (node.m_key, value, hashcode, node.m_next); |
| | | if (prev == null) { |
| | | buckets [bucketNo] = newNode; |
| | | } else { |
| | | prev.m_next = newNode; |
| | | } |
| | | resultingValue = value; |
| | | } else { |
| | | resultingValue = node.m_value; |
| | | } |
| | | return false; |
| | | } |
| | | prev = node; |
| | | } |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets[bucketNo]; node != null; node = node.m_next) |
| | | { |
| | | Assert((prev == null && node == m_buckets[bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals(node.m_key, key)) |
| | | { |
| | | // The key was found in the dictionary. If updates are allowed, update the value for that key. |
| | | // We need to create a new node for the update, in order to support TValue types that cannot |
| | | // be written atomically, since lock-free reads may be happening concurrently. |
| | | if (updateIfExists) |
| | | { |
| | | Node newNode = new Node(node.m_key, value, hashcode, node.m_next); |
| | | if (prev == null) |
| | | { |
| | | buckets[bucketNo] = newNode; |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = newNode; |
| | | } |
| | | resultingValue = value; |
| | | } |
| | | else |
| | | { |
| | | resultingValue = node.m_value; |
| | | } |
| | | return false; |
| | | } |
| | | prev = node; |
| | | } |
| | | |
| | | // The key was not found in the bucket. Insert the key-value pair. |
| | | buckets [bucketNo] = new Node (key, value, hashcode, buckets [bucketNo]); |
| | | checked { |
| | | m_countPerLock [lockNo]++; |
| | | } |
| | | // The key was not found in the bucket. Insert the key-value pair. |
| | | buckets[bucketNo] = new Node(key, value, hashcode, buckets[bucketNo]); |
| | | checked |
| | | { |
| | | m_countPerLock[lockNo]++; |
| | | } |
| | | |
| | | // |
| | | // If this lock has element / bucket ratio greater than 1, resize the entire table. |
| | | // Note: the formula is chosen to avoid overflow, but has a small inaccuracy due to |
| | | // rounding. |
| | | // |
| | | if (m_countPerLock [lockNo] > buckets.Length / m_locks.Length) { |
| | | resizeDesired = true; |
| | | } |
| | | } finally { |
| | | if (lockTaken) |
| | | Monitor.Exit (m_locks [lockNo]); |
| | | } |
| | | // |
| | | // If this lock has element / bucket ratio greater than 1, resize the entire table. |
| | | // Note: the formula is chosen to avoid overflow, but has a small inaccuracy due to |
| | | // rounding. |
| | | // |
| | | if (m_countPerLock[lockNo] > buckets.Length / m_locks.Length) |
| | | { |
| | | resizeDesired = true; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | if (lockTaken) |
| | | Monitor.Exit(m_locks[lockNo]); |
| | | } |
| | | |
| | | // |
| | | // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table. |
| | | // |
| | | // Concurrency notes: |
| | | // - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks. |
| | | // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0 |
| | | // and then verify that the table we passed to it as the argument is still the current table. |
| | | // |
| | | if (resizeDesired) { |
| | | GrowTable (buckets); |
| | | } |
| | | // |
| | | // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table. |
| | | // |
| | | // Concurrency notes: |
| | | // - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks. |
| | | // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0 |
| | | // and then verify that the table we passed to it as the argument is still the current table. |
| | | // |
| | | if (resizeDesired) |
| | | { |
| | | GrowTable(buckets); |
| | | } |
| | | |
| | | resultingValue = value; |
| | | return true; |
| | | } |
| | | } |
| | | resultingValue = value; |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets or sets the value associated with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get or set.</param> |
| | | /// <value>The value associated with the specified key. If the specified key is not found, a get |
| | | /// operation throws a |
| | | /// <see cref="T:Sytem.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new |
| | | /// element with the specified key.</value> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and |
| | | /// <paramref name="key"/> |
| | | /// does not exist in the collection.</exception> |
| | | public TValue this [TKey key] { |
| | | get { |
| | | TValue value; |
| | | if (!TryGetValue (key, out value)) { |
| | | throw new KeyNotFoundException (); |
| | | } |
| | | return value; |
| | | } |
| | | set { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | TValue dummy; |
| | | TryAddInternal (key, value, true, true, out dummy); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Gets or sets the value associated with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get or set.</param> |
| | | /// <value>The value associated with the specified key. If the specified key is not found, a get |
| | | /// operation throws a |
| | | /// <see cref="T:Sytem.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new |
| | | /// element with the specified key.</value> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and |
| | | /// <paramref name="key"/> |
| | | /// does not exist in the collection.</exception> |
| | | public TValue this[TKey key] |
| | | { |
| | | get |
| | | { |
| | | TValue value; |
| | | if (!TryGetValue(key, out value)) |
| | | { |
| | | throw new KeyNotFoundException(); |
| | | } |
| | | return value; |
| | | } |
| | | set |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | TValue dummy; |
| | | TryAddInternal(key, value, true, true, out dummy); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets the number of key/value pairs contained in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <value>The number of key/value paris contained in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</value> |
| | | /// <remarks>Count has snapshot semantics and represents the number of items in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// at the moment when Count was accessed.</remarks> |
| | | public int Count { |
| | | get { |
| | | int count = 0; |
| | | /// <summary> |
| | | /// Gets the number of key/value pairs contained in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <value>The number of key/value paris contained in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</value> |
| | | /// <remarks>Count has snapshot semantics and represents the number of items in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// at the moment when Count was accessed.</remarks> |
| | | public int Count |
| | | { |
| | | get |
| | | { |
| | | int count = 0; |
| | | |
| | | int acquiredLocks = 0; |
| | | try { |
| | | // Acquire all locks |
| | | AcquireAllLocks (ref acquiredLocks); |
| | | int acquiredLocks = 0; |
| | | try |
| | | { |
| | | // Acquire all locks |
| | | AcquireAllLocks(ref acquiredLocks); |
| | | |
| | | // Compute the count, we allow overflow |
| | | for (int i = 0; i < m_countPerLock.Length; i++) { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | // Compute the count, we allow overflow |
| | | for (int i = 0; i < m_countPerLock.Length; i++) |
| | | { |
| | | count += m_countPerLock[i]; |
| | | } |
| | | |
| | | } finally { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks (0, acquiredLocks); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks(0, acquiredLocks); |
| | | } |
| | | |
| | | return count; |
| | | } |
| | | } |
| | | return count; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// if the key does not already exist. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="valueFactory">The function used to generate a value for the key</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="valueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The value for the key. This will be either the existing value for the key if the |
| | | /// key is already in the dictionary, or the new value for the key as returned by valueFactory |
| | | /// if the key was not in the dictionary.</returns> |
| | | public TValue GetOrAdd (TKey key, Func<TKey, TValue> valueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | if (valueFactory == null) |
| | | throw new ArgumentNullException ("valueFactory"); |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// if the key does not already exist. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="valueFactory">The function used to generate a value for the key</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="valueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The value for the key. This will be either the existing value for the key if the |
| | | /// key is already in the dictionary, or the new value for the key as returned by valueFactory |
| | | /// if the key was not in the dictionary.</returns> |
| | | public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | if (valueFactory == null) |
| | | throw new ArgumentNullException("valueFactory"); |
| | | |
| | | TValue resultingValue; |
| | | if (TryGetValue (key, out resultingValue)) { |
| | | return resultingValue; |
| | | } |
| | | TryAddInternal (key, valueFactory (key), false, true, out resultingValue); |
| | | return resultingValue; |
| | | } |
| | | TValue resultingValue; |
| | | if (TryGetValue(key, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | TryAddInternal(key, valueFactory(key), false, true, out resultingValue); |
| | | return resultingValue; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// if the key does not already exist. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="value">the value to be added, if the key does not already exist</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The value for the key. This will be either the existing value for the key if the |
| | | /// key is already in the dictionary, or the new value if the key was not in the dictionary.</returns> |
| | | public TValue GetOrAdd (TKey key, TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// if the key does not already exist. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to add.</param> |
| | | /// <param name="value">the value to be added, if the key does not already exist</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The value for the key. This will be either the existing value for the key if the |
| | | /// key is already in the dictionary, or the new value if the key was not in the dictionary.</returns> |
| | | public TValue GetOrAdd(TKey key, TValue value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | TValue resultingValue; |
| | | TryAddInternal (key, value, false, true, out resultingValue); |
| | | return resultingValue; |
| | | } |
| | | TValue resultingValue; |
| | | TryAddInternal(key, value, false, true, out resultingValue); |
| | | return resultingValue; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already |
| | | /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key |
| | | /// already exists. |
| | | /// </summary> |
| | | /// <param name="key">The key to be added or whose value should be updated</param> |
| | | /// <param name="addValueFactory">The function used to generate a value for an absent key</param> |
| | | /// <param name="updateValueFactory">The function used to generate a new value for an existing key |
| | | /// based on the key's existing value</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="addValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was |
| | | /// absent) or the result of updateValueFactory (if the key was present).</returns> |
| | | public TValue AddOrUpdate (TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | if (addValueFactory == null) |
| | | throw new ArgumentNullException ("addValueFactory"); |
| | | if (updateValueFactory == null) |
| | | throw new ArgumentNullException ("updateValueFactory"); |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already |
| | | /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key |
| | | /// already exists. |
| | | /// </summary> |
| | | /// <param name="key">The key to be added or whose value should be updated</param> |
| | | /// <param name="addValueFactory">The function used to generate a value for an absent key</param> |
| | | /// <param name="updateValueFactory">The function used to generate a new value for an existing key |
| | | /// based on the key's existing value</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="addValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was |
| | | /// absent) or the result of updateValueFactory (if the key was present).</returns> |
| | | public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | if (addValueFactory == null) |
| | | throw new ArgumentNullException("addValueFactory"); |
| | | if (updateValueFactory == null) |
| | | throw new ArgumentNullException("updateValueFactory"); |
| | | |
| | | TValue newValue, resultingValue; |
| | | while (true) { |
| | | TValue oldValue; |
| | | if (TryGetValue (key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory (key, oldValue); |
| | | if (TryUpdate (key, newValue, oldValue)) { |
| | | return newValue; |
| | | } |
| | | } else { //try add |
| | | newValue = addValueFactory (key); |
| | | if (TryAddInternal (key, newValue, false, true, out resultingValue)) { |
| | | return resultingValue; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | TValue newValue, resultingValue; |
| | | while (true) |
| | | { |
| | | TValue oldValue; |
| | | if (TryGetValue(key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory(key, oldValue); |
| | | if (TryUpdate(key, newValue, oldValue)) |
| | | { |
| | | return newValue; |
| | | } |
| | | } |
| | | else |
| | | { //try add |
| | | newValue = addValueFactory(key); |
| | | if (TryAddInternal(key, newValue, false, true, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already |
| | | /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key |
| | | /// already exists. |
| | | /// </summary> |
| | | /// <param name="key">The key to be added or whose value should be updated</param> |
| | | /// <param name="addValue">The value to be added for an absent key</param> |
| | | /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on |
| | | /// the key's existing value</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was |
| | | /// absent) or the result of updateValueFactory (if the key was present).</returns> |
| | | public TValue AddOrUpdate (TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | if (updateValueFactory == null) |
| | | throw new ArgumentNullException ("updateValueFactory"); |
| | | TValue newValue, resultingValue; |
| | | while (true) { |
| | | TValue oldValue; |
| | | if (TryGetValue (key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory (key, oldValue); |
| | | if (TryUpdate (key, newValue, oldValue)) { |
| | | return newValue; |
| | | } |
| | | } else { //try add |
| | | if (TryAddInternal (key, addValue, false, true, out resultingValue)) { |
| | | return resultingValue; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already |
| | | /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key |
| | | /// already exists. |
| | | /// </summary> |
| | | /// <param name="key">The key to be added or whose value should be updated</param> |
| | | /// <param name="addValue">The value to be added for an absent key</param> |
| | | /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on |
| | | /// the key's existing value</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was |
| | | /// absent) or the result of updateValueFactory (if the key was present).</returns> |
| | | public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | if (updateValueFactory == null) |
| | | throw new ArgumentNullException("updateValueFactory"); |
| | | TValue newValue, resultingValue; |
| | | while (true) |
| | | { |
| | | TValue oldValue; |
| | | if (TryGetValue(key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory(key, oldValue); |
| | | if (TryUpdate(key, newValue, oldValue)) |
| | | { |
| | | return newValue; |
| | | } |
| | | } |
| | | else |
| | | { //try add |
| | | if (TryAddInternal(key, addValue, false, true, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// Gets a value that indicates whether the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty; otherwise, |
| | | /// false.</value> |
| | | public bool IsEmpty { |
| | | get { |
| | | int acquiredLocks = 0; |
| | | try { |
| | | // Acquire all locks |
| | | AcquireAllLocks (ref acquiredLocks); |
| | | /// <summary> |
| | | /// Gets a value that indicates whether the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty; otherwise, |
| | | /// false.</value> |
| | | public bool IsEmpty |
| | | { |
| | | get |
| | | { |
| | | int acquiredLocks = 0; |
| | | try |
| | | { |
| | | // Acquire all locks |
| | | AcquireAllLocks(ref acquiredLocks); |
| | | |
| | | for (int i = 0; i < m_countPerLock.Length; i++) { |
| | | if (m_countPerLock [i] != 0) { |
| | | return false; |
| | | } |
| | | } |
| | | } finally { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks (0, acquiredLocks); |
| | | } |
| | | for (int i = 0; i < m_countPerLock.Length; i++) |
| | | { |
| | | if (m_countPerLock[i] != 0) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks(0, acquiredLocks); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | #region IDictionary<TKey,TValue> members |
| | | #region IDictionary<TKey,TValue> members |
| | | |
| | | /// <summary> |
| | | /// Adds the specified key and value to the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The object to use as the key of the element to add.</param> |
| | | /// <param name="value">The object to use as the value of the element to add.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// An element with the same key already exists in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</exception> |
| | | void IDictionary<TKey, TValue>.Add (TKey key, TValue value) |
| | | { |
| | | if (!TryAdd (key, value)) { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_KeyAlreadyExisted")); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Adds the specified key and value to the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The object to use as the key of the element to add.</param> |
| | | /// <param name="value">The object to use as the value of the element to add.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// An element with the same key already exists in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</exception> |
| | | void IDictionary<TKey, TValue>.Add(TKey key, TValue value) |
| | | { |
| | | if (!TryAdd(key, value)) |
| | | { |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_KeyAlreadyExisted")); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Removes the element with the specified key from the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove.</param> |
| | | /// <returns>true if the element is successfully remove; otherwise false. This method also returns |
| | | /// false if |
| | | /// <paramref name="key"/> was not found in the original <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | bool IDictionary<TKey, TValue>.Remove (TKey key) |
| | | { |
| | | TValue throwAwayValue; |
| | | return TryRemove (key, out throwAwayValue); |
| | | } |
| | | /// <summary> |
| | | /// Removes the element with the specified key from the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove.</param> |
| | | /// <returns>true if the element is successfully remove; otherwise false. This method also returns |
| | | /// false if |
| | | /// <paramref name="key"/> was not found in the original <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </returns> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | bool IDictionary<TKey, TValue>.Remove(TKey key) |
| | | { |
| | | TValue throwAwayValue; |
| | | return TryRemove(key, out throwAwayValue); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a collection containing the keys in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}"/> containing the keys in the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TKey> Keys { |
| | | get { return GetKeys (); } |
| | | } |
| | | /// <summary> |
| | | /// Gets a collection containing the keys in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}"/> containing the keys in the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TKey> Keys |
| | | { |
| | | get { return GetKeys(); } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a collection containing the values in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TValue}"/> containing the values in |
| | | /// the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TValue> Values { |
| | | get { return GetValues (); } |
| | | } |
| | | /// <summary> |
| | | /// Gets a collection containing the values in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TValue}"/> containing the values in |
| | | /// the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TValue> Values |
| | | { |
| | | get { return GetValues(); } |
| | | } |
| | | |
| | | #endregion |
| | | #endregion |
| | | |
| | | #region ICollection<KeyValuePair<TKey,TValue>> Members |
| | | #region ICollection<KeyValuePair<TKey,TValue>> Members |
| | | |
| | | /// <summary> |
| | | /// Adds the specified value to the <see cref="T:System.Collections.Generic.ICollection{TValue}"/> |
| | | /// with the specified key. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure representing the key and value to add to the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> |
| | | /// <exception cref="T:System.ArgumentNullException">The <paramref name="keyValuePair"/> of <paramref |
| | | /// name="keyValuePair"/> is null.</exception> |
| | | /// <exception cref="T:System.OverflowException">The <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/> |
| | | /// contains too many elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/></exception> |
| | | void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | ((IDictionary<TKey, TValue>)this).Add (keyValuePair.Key, keyValuePair.Value); |
| | | } |
| | | /// <summary> |
| | | /// Adds the specified value to the <see cref="T:System.Collections.Generic.ICollection{TValue}"/> |
| | | /// with the specified key. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure representing the key and value to add to the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> |
| | | /// <exception cref="T:System.ArgumentNullException">The <paramref name="keyValuePair"/> of <paramref |
| | | /// name="keyValuePair"/> is null.</exception> |
| | | /// <exception cref="T:System.OverflowException">The <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/> |
| | | /// contains too many elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/></exception> |
| | | void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | ((IDictionary<TKey, TValue>)this).Add(keyValuePair.Key, keyValuePair.Value); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Determines whether the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> |
| | | /// contains a specific key and value. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure to locate in the <see |
| | | /// cref="T:System.Collections.Generic.ICollection{TValue}"/>.</param> |
| | | /// <returns>true if the <paramref name="keyValuePair"/> is found in the <see |
| | | /// cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/>; otherwise, false.</returns> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | TValue value; |
| | | if (!TryGetValue (keyValuePair.Key, out value)) { |
| | | return false; |
| | | } |
| | | return EqualityComparer<TValue>.Default.Equals (value, keyValuePair.Value); |
| | | } |
| | | /// <summary> |
| | | /// Determines whether the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> |
| | | /// contains a specific key and value. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure to locate in the <see |
| | | /// cref="T:System.Collections.Generic.ICollection{TValue}"/>.</param> |
| | | /// <returns>true if the <paramref name="keyValuePair"/> is found in the <see |
| | | /// cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/>; otherwise, false.</returns> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | TValue value; |
| | | if (!TryGetValue(keyValuePair.Key, out value)) |
| | | { |
| | | return false; |
| | | } |
| | | return EqualityComparer<TValue>.Default.Equals(value, keyValuePair.Value); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a value indicating whether the dictionary is read-only. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> is |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>, this property always returns |
| | | /// false.</value> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { |
| | | get { return false; } |
| | | } |
| | | /// <summary> |
| | | /// Gets a value indicating whether the dictionary is read-only. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> is |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>, this property always returns |
| | | /// false.</value> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Removes a key and value from the dictionary. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure representing the key and value to remove from the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> |
| | | /// <returns>true if the key and value represented by <paramref name="keyValuePair"/> is successfully |
| | | /// found and removed; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException">The Key property of <paramref |
| | | /// name="keyValuePair"/> is a null reference (Nothing in Visual Basic).</exception> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | if (keyValuePair.Key == null) |
| | | throw new ArgumentNullException (GetResource ("ConcurrentDictionary_ItemKeyIsNull")); |
| | | /// <summary> |
| | | /// Removes a key and value from the dictionary. |
| | | /// </summary> |
| | | /// <param name="keyValuePair">The <see |
| | | /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> |
| | | /// structure representing the key and value to remove from the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> |
| | | /// <returns>true if the key and value represented by <paramref name="keyValuePair"/> is successfully |
| | | /// found and removed; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException">The Key property of <paramref |
| | | /// name="keyValuePair"/> is a null reference (Nothing in Visual Basic).</exception> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | if (keyValuePair.Key == null) |
| | | throw new ArgumentNullException(GetResource("ConcurrentDictionary_ItemKeyIsNull")); |
| | | |
| | | TValue throwAwayValue; |
| | | return TryRemoveInternal (keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value); |
| | | } |
| | | TValue throwAwayValue; |
| | | return TryRemoveInternal(keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value); |
| | | } |
| | | |
| | | #endregion |
| | | #endregion |
| | | |
| | | #region IEnumerable Members |
| | | #region IEnumerable Members |
| | | |
| | | /// <summary>Returns an enumerator that iterates through the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | /// <remarks> |
| | | /// The enumerator returned from the dictionary is safe to use concurrently with |
| | | /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot |
| | | /// of the dictionary. The contents exposed through the enumerator may contain modifications |
| | | /// made to the dictionary after <see cref="GetEnumerator"/> was called. |
| | | /// </remarks> |
| | | IEnumerator IEnumerable.GetEnumerator () |
| | | { |
| | | return ((ConcurrentDictionary<TKey, TValue>)this).GetEnumerator (); |
| | | } |
| | | /// <summary>Returns an enumerator that iterates through the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> |
| | | /// <remarks> |
| | | /// The enumerator returned from the dictionary is safe to use concurrently with |
| | | /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot |
| | | /// of the dictionary. The contents exposed through the enumerator may contain modifications |
| | | /// made to the dictionary after <see cref="GetEnumerator"/> was called. |
| | | /// </remarks> |
| | | IEnumerator IEnumerable.GetEnumerator() |
| | | { |
| | | return ((ConcurrentDictionary<TKey, TValue>)this).GetEnumerator(); |
| | | } |
| | | |
| | | #endregion |
| | | #endregion |
| | | |
| | | #region IDictionary Members |
| | | #region IDictionary Members |
| | | |
| | | /// <summary> |
| | | /// Adds the specified key and value to the dictionary. |
| | | /// </summary> |
| | | /// <param name="key">The object to use as the key.</param> |
| | | /// <param name="value">The object to use as the value.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// <paramref name="key"/> is of a type that is not assignable to the key type <typeparamref |
| | | /// name="TKey"/> of the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. -or- |
| | | /// <paramref name="value"/> is of a type that is not assignable to <typeparamref name="TValue"/>, |
| | | /// the type of values in the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// -or- A value with the same key already exists in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </exception> |
| | | void IDictionary.Add (object key, object value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | if (!(key is TKey)) |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfKeyIncorrect")); |
| | | /// <summary> |
| | | /// Adds the specified key and value to the dictionary. |
| | | /// </summary> |
| | | /// <param name="key">The object to use as the key.</param> |
| | | /// <param name="value">The object to use as the value.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.OverflowException">The dictionary contains too many |
| | | /// elements.</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// <paramref name="key"/> is of a type that is not assignable to the key type <typeparamref |
| | | /// name="TKey"/> of the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. -or- |
| | | /// <paramref name="value"/> is of a type that is not assignable to <typeparamref name="TValue"/>, |
| | | /// the type of values in the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// -or- A value with the same key already exists in the <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. |
| | | /// </exception> |
| | | void IDictionary.Add(object key, object value) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | if (!(key is TKey)) |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect")); |
| | | |
| | | TValue typedValue; |
| | | try { |
| | | typedValue = (TValue)value; |
| | | } catch (InvalidCastException) { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfValueIncorrect")); |
| | | } |
| | | TValue typedValue; |
| | | try |
| | | { |
| | | typedValue = (TValue)value; |
| | | } |
| | | catch (InvalidCastException) |
| | | { |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect")); |
| | | } |
| | | |
| | | ((IDictionary<TKey, TValue>)this).Add ((TKey)key, typedValue); |
| | | } |
| | | ((IDictionary<TKey, TValue>)this).Add((TKey)key, typedValue); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets whether the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains an |
| | | /// element with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key to locate in the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</param> |
| | | /// <returns>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains |
| | | /// an element with the specified key; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"> <paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | bool IDictionary.Contains (object key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Gets whether the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains an |
| | | /// element with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key to locate in the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</param> |
| | | /// <returns>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains |
| | | /// an element with the specified key; otherwise, false.</returns> |
| | | /// <exception cref="T:System.ArgumentNullException"> <paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | bool IDictionary.Contains(object key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | return (key is TKey) && ((ConcurrentDictionary<TKey, TValue>)this).ContainsKey ((TKey)key); |
| | | } |
| | | return (key is TKey) && ((ConcurrentDictionary<TKey, TValue>)this).ContainsKey((TKey)key); |
| | | } |
| | | |
| | | /// <summary>Provides an <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the |
| | | /// <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</returns> |
| | | IDictionaryEnumerator IDictionary.GetEnumerator () |
| | | { |
| | | return new DictionaryEnumerator (this); |
| | | } |
| | | /// <summary>Provides an <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the |
| | | /// <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</summary> |
| | | /// <returns>An <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</returns> |
| | | IDictionaryEnumerator IDictionary.GetEnumerator() |
| | | { |
| | | return new DictionaryEnumerator(this); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a value indicating whether the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a fixed size. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a |
| | | /// fixed size; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsFixedSize { |
| | | get { return false; } |
| | | } |
| | | /// <summary> |
| | | /// Gets a value indicating whether the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a fixed size. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a |
| | | /// fixed size; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsFixedSize |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a value indicating whether the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is read-only. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsReadOnly { |
| | | get { return false; } |
| | | } |
| | | /// <summary> |
| | | /// Gets a value indicating whether the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is read-only. |
| | | /// </summary> |
| | | /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsReadOnly |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets an <see cref="T:System.Collections.ICollection"/> containing the keys of the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the keys of the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</value> |
| | | ICollection IDictionary.Keys { |
| | | get { return GetKeys (); } |
| | | } |
| | | /// <summary> |
| | | /// Gets an <see cref="T:System.Collections.ICollection"/> containing the keys of the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the keys of the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</value> |
| | | ICollection IDictionary.Keys |
| | | { |
| | | get { return GetKeys(); } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Removes the element with the specified key from the <see |
| | | /// cref="T:System.Collections.IDictionary"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | void IDictionary.Remove (object key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Removes the element with the specified key from the <see |
| | | /// cref="T:System.Collections.IDictionary"/>. |
| | | /// </summary> |
| | | /// <param name="key">The key of the element to remove.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | void IDictionary.Remove(object key) |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | TValue throwAwayValue; |
| | | if (key is TKey) { |
| | | this.TryRemove ((TKey)key, out throwAwayValue); |
| | | } |
| | | } |
| | | TValue throwAwayValue; |
| | | if (key is TKey) |
| | | { |
| | | this.TryRemove((TKey)key, out throwAwayValue); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets an <see cref="T:System.Collections.ICollection"/> containing the values in the <see |
| | | /// cref="T:System.Collections.IDictionary"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the values in the <see |
| | | /// cref="T:System.Collections.IDictionary"/>.</value> |
| | | ICollection IDictionary.Values { |
| | | get { return GetValues (); } |
| | | } |
| | | /// <summary> |
| | | /// Gets an <see cref="T:System.Collections.ICollection"/> containing the values in the <see |
| | | /// cref="T:System.Collections.IDictionary"/>. |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the values in the <see |
| | | /// cref="T:System.Collections.IDictionary"/>.</value> |
| | | ICollection IDictionary.Values |
| | | { |
| | | get { return GetValues(); } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets or sets the value associated with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get or set.</param> |
| | | /// <value>The value associated with the specified key, or a null reference (Nothing in Visual Basic) |
| | | /// if <paramref name="key"/> is not in the dictionary or <paramref name="key"/> is of a type that is |
| | | /// not assignable to the key type <typeparamref name="TKey"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>.</value> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// A value is being assigned, and <paramref name="key"/> is of a type that is not assignable to the |
| | | /// key type <typeparamref name="TKey"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>. -or- A value is being |
| | | /// assigned, and <paramref name="key"/> is of a type that is not assignable to the value type |
| | | /// <typeparamref name="TValue"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/> |
| | | /// </exception> |
| | | object IDictionary.this [object key] { |
| | | get { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | /// <summary> |
| | | /// Gets or sets the value associated with the specified key. |
| | | /// </summary> |
| | | /// <param name="key">The key of the value to get or set.</param> |
| | | /// <value>The value associated with the specified key, or a null reference (Nothing in Visual Basic) |
| | | /// if <paramref name="key"/> is not in the dictionary or <paramref name="key"/> is of a type that is |
| | | /// not assignable to the key type <typeparamref name="TKey"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>.</value> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentException"> |
| | | /// A value is being assigned, and <paramref name="key"/> is of a type that is not assignable to the |
| | | /// key type <typeparamref name="TKey"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>. -or- A value is being |
| | | /// assigned, and <paramref name="key"/> is of a type that is not assignable to the value type |
| | | /// <typeparamref name="TValue"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/> |
| | | /// </exception> |
| | | object IDictionary.this[object key] |
| | | { |
| | | get |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | TValue value; |
| | | if (key is TKey && this.TryGetValue ((TKey)key, out value)) { |
| | | return value; |
| | | } |
| | | TValue value; |
| | | if (key is TKey && this.TryGetValue((TKey)key, out value)) |
| | | { |
| | | return value; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | set { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | return null; |
| | | } |
| | | set |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException("key"); |
| | | |
| | | if (!(key is TKey)) |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfKeyIncorrect")); |
| | | if (!(value is TValue)) |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfValueIncorrect")); |
| | | if (!(key is TKey)) |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect")); |
| | | if (!(value is TValue)) |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect")); |
| | | |
| | | ((ConcurrentDictionary<TKey, TValue>)this) [(TKey)key] = (TValue)value; |
| | | } |
| | | } |
| | | ((ConcurrentDictionary<TKey, TValue>)this)[(TKey)key] = (TValue)value; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | #endregion |
| | | |
| | | #region ICollection Members |
| | | #region ICollection Members |
| | | |
| | | /// <summary> |
| | | /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an array, starting |
| | | /// at the specified array index. |
| | | /// </summary> |
| | | /// <param name="array">The one-dimensional array that is the destination of the elements copied from |
| | | /// the <see cref="T:System.Collections.ICollection"/>. The array must have zero-based |
| | | /// indexing.</param> |
| | | /// <param name="index">The zero-based index in <paramref name="array"/> at which copying |
| | | /// begins.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than |
| | | /// 0.</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than |
| | | /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see |
| | | /// cref="T:System.Collections.ICollection"/> |
| | | /// is greater than the available space from <paramref name="index"/> to the end of the destination |
| | | /// <paramref name="array"/>.</exception> |
| | | void ICollection.CopyTo (Array array, int index) |
| | | { |
| | | if (array == null) |
| | | throw new ArgumentNullException ("array"); |
| | | if (index < 0) |
| | | throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative")); |
| | | /// <summary> |
| | | /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an array, starting |
| | | /// at the specified array index. |
| | | /// </summary> |
| | | /// <param name="array">The one-dimensional array that is the destination of the elements copied from |
| | | /// the <see cref="T:System.Collections.ICollection"/>. The array must have zero-based |
| | | /// indexing.</param> |
| | | /// <param name="index">The zero-based index in <paramref name="array"/> at which copying |
| | | /// begins.</param> |
| | | /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference |
| | | /// (Nothing in Visual Basic).</exception> |
| | | /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than |
| | | /// 0.</exception> |
| | | /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than |
| | | /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see |
| | | /// cref="T:System.Collections.ICollection"/> |
| | | /// is greater than the available space from <paramref name="index"/> to the end of the destination |
| | | /// <paramref name="array"/>.</exception> |
| | | void ICollection.CopyTo(Array array, int index) |
| | | { |
| | | if (array == null) |
| | | throw new ArgumentNullException("array"); |
| | | if (index < 0) |
| | | throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative")); |
| | | |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | |
| | | int count = 0; |
| | | int count = 0; |
| | | |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock[i]; |
| | | } |
| | | |
| | | if (array.Length - count < index || count < 0) { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | if (array.Length - count < index || count < 0) |
| | | { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | |
| | | // To be consistent with the behavior of ICollection.CopyTo() in Dictionary<TKey,TValue>, |
| | | // we recognize three types of target arrays: |
| | | // - an array of KeyValuePair<TKey, TValue> structs |
| | | // - an array of DictionaryEntry structs |
| | | // - an array of objects |
| | | // To be consistent with the behavior of ICollection.CopyTo() in Dictionary<TKey,TValue>, |
| | | // we recognize three types of target arrays: |
| | | // - an array of KeyValuePair<TKey, TValue> structs |
| | | // - an array of DictionaryEntry structs |
| | | // - an array of objects |
| | | |
| | | KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[]; |
| | | if (pairs != null) { |
| | | CopyToPairs (pairs, index); |
| | | return; |
| | | } |
| | | KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[]; |
| | | if (pairs != null) |
| | | { |
| | | CopyToPairs(pairs, index); |
| | | return; |
| | | } |
| | | |
| | | DictionaryEntry[] entries = array as DictionaryEntry[]; |
| | | if (entries != null) { |
| | | CopyToEntries (entries, index); |
| | | return; |
| | | } |
| | | DictionaryEntry[] entries = array as DictionaryEntry[]; |
| | | if (entries != null) |
| | | { |
| | | CopyToEntries(entries, index); |
| | | return; |
| | | } |
| | | |
| | | object[] objects = array as object[]; |
| | | if (objects != null) { |
| | | CopyToObjects (objects, index); |
| | | return; |
| | | } |
| | | object[] objects = array as object[]; |
| | | if (objects != null) |
| | | { |
| | | CopyToObjects(objects, index); |
| | | return; |
| | | } |
| | | |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayIncorrectType"), "array"); |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayIncorrectType"), "array"); |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is |
| | | /// synchronized with the SyncRoot. |
| | | /// </summary> |
| | | /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized |
| | | /// (thread safe); otherwise, false. For <see |
| | | /// cref="T:System.Collections.Concurrent.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool ICollection.IsSynchronized { |
| | | get { return false; } |
| | | } |
| | | /// <summary> |
| | | /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is |
| | | /// synchronized with the SyncRoot. |
| | | /// </summary> |
| | | /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized |
| | | /// (thread safe); otherwise, false. For <see |
| | | /// cref="T:System.Collections.Concurrent.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool ICollection.IsSynchronized |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets an object that can be used to synchronize access to the <see |
| | | /// cref="T:System.Collections.ICollection"/>. This property is not supported. |
| | | /// </summary> |
| | | /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception> |
| | | object ICollection.SyncRoot { |
| | | get { |
| | | throw new NotSupportedException (); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// Gets an object that can be used to synchronize access to the <see |
| | | /// cref="T:System.Collections.ICollection"/>. This property is not supported. |
| | | /// </summary> |
| | | /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception> |
| | | object ICollection.SyncRoot |
| | | { |
| | | get |
| | | { |
| | | throw new NotSupportedException(); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | #endregion |
| | | |
| | | /// <summary> |
| | | /// Replaces the internal table with a larger one. To prevent multiple threads from resizing the |
| | | /// table as a result of ----s, the table of buckets that was deemed too small is passed in as |
| | | /// an argument to GrowTable(). GrowTable() obtains a lock, and then checks whether the bucket |
| | | /// table has been replaced in the meantime or not. |
| | | /// </summary> |
| | | /// <param name="buckets">Reference to the bucket table that was deemed too small.</param> |
| | | private void GrowTable (Node[] buckets) |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | // The thread that first obtains m_locks[0] will be the one doing the resize operation |
| | | AcquireLocks (0, 1, ref locksAcquired); |
| | | /// <summary> |
| | | /// Replaces the internal table with a larger one. To prevent multiple threads from resizing the |
| | | /// table as a result of ----s, the table of buckets that was deemed too small is passed in as |
| | | /// an argument to GrowTable(). GrowTable() obtains a lock, and then checks whether the bucket |
| | | /// table has been replaced in the meantime or not. |
| | | /// </summary> |
| | | /// <param name="buckets">Reference to the bucket table that was deemed too small.</param> |
| | | private void GrowTable(Node[] buckets) |
| | | { |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | // The thread that first obtains m_locks[0] will be the one doing the resize operation |
| | | AcquireLocks(0, 1, ref locksAcquired); |
| | | |
| | | // Make sure nobody resized the table while we were waiting for lock 0: |
| | | if (buckets != m_buckets) { |
| | | // We assume that since the table reference is different, it was already resized. If we ever |
| | | // decide to do table shrinking, or replace the table for other reasons, we will have to revisit |
| | | // this logic. |
| | | return; |
| | | } |
| | | // Make sure nobody resized the table while we were waiting for lock 0: |
| | | if (buckets != m_buckets) |
| | | { |
| | | // We assume that since the table reference is different, it was already resized. If we ever |
| | | // decide to do table shrinking, or replace the table for other reasons, we will have to revisit |
| | | // this logic. |
| | | return; |
| | | } |
| | | |
| | | // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by |
| | | // 2,3,5 or 7. We can consider a different table-sizing policy in the future. |
| | | int newLength; |
| | | try { |
| | | checked { |
| | | // Double the size of the buckets table and add one, so that we have an odd integer. |
| | | newLength = buckets.Length * 2 + 1; |
| | | // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by |
| | | // 2,3,5 or 7. We can consider a different table-sizing policy in the future. |
| | | int newLength; |
| | | try |
| | | { |
| | | checked |
| | | { |
| | | // Double the size of the buckets table and add one, so that we have an odd integer. |
| | | newLength = buckets.Length * 2 + 1; |
| | | |
| | | // Now, we only need to check odd integers, and find the first that is not divisible |
| | | // by 3, 5 or 7. |
| | | while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) { |
| | | newLength += 2; |
| | | } |
| | | // Now, we only need to check odd integers, and find the first that is not divisible |
| | | // by 3, 5 or 7. |
| | | while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) |
| | | { |
| | | newLength += 2; |
| | | } |
| | | |
| | | Assert (newLength % 2 != 0); |
| | | } |
| | | } catch (OverflowException) { |
| | | // If we were to resize the table, its new size will not fit into a 32-bit signed int. Just return. |
| | | return; |
| | | } |
| | | Assert(newLength % 2 != 0); |
| | | } |
| | | } |
| | | catch (OverflowException) |
| | | { |
| | | // If we were to resize the table, its new size will not fit into a 32-bit signed int. Just return. |
| | | return; |
| | | } |
| | | |
| | | Node[] newBuckets = new Node[newLength]; |
| | | int[] newCountPerLock = new int[m_locks.Length]; |
| | | Node[] newBuckets = new Node[newLength]; |
| | | int[] newCountPerLock = new int[m_locks.Length]; |
| | | |
| | | // Now acquire all other locks for the table |
| | | AcquireLocks (1, m_locks.Length, ref locksAcquired); |
| | | // Now acquire all other locks for the table |
| | | AcquireLocks(1, m_locks.Length, ref locksAcquired); |
| | | |
| | | // Copy all data into a new table, creating new nodes for all elements |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | Node current = buckets [i]; |
| | | while (current != null) { |
| | | Node next = current.m_next; |
| | | int newBucketNo, newLockNo; |
| | | GetBucketAndLockNo (current.m_hashcode, out newBucketNo, out newLockNo, newBuckets.Length); |
| | | // Copy all data into a new table, creating new nodes for all elements |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | Node current = buckets[i]; |
| | | while (current != null) |
| | | { |
| | | Node next = current.m_next; |
| | | int newBucketNo, newLockNo; |
| | | GetBucketAndLockNo(current.m_hashcode, out newBucketNo, out newLockNo, newBuckets.Length); |
| | | |
| | | newBuckets [newBucketNo] = new Node (current.m_key, current.m_value, current.m_hashcode, newBuckets [newBucketNo]); |
| | | newBuckets[newBucketNo] = new Node(current.m_key, current.m_value, current.m_hashcode, newBuckets[newBucketNo]); |
| | | |
| | | checked { |
| | | newCountPerLock [newLockNo]++; |
| | | } |
| | | checked |
| | | { |
| | | newCountPerLock[newLockNo]++; |
| | | } |
| | | |
| | | current = next; |
| | | } |
| | | } |
| | | current = next; |
| | | } |
| | | } |
| | | |
| | | // And finally adjust m_buckets and m_countPerLock to point to data for the new table |
| | | m_buckets = newBuckets; |
| | | m_countPerLock = newCountPerLock; |
| | | // And finally adjust m_buckets and m_countPerLock to point to data for the new table |
| | | m_buckets = newBuckets; |
| | | m_countPerLock = newCountPerLock; |
| | | |
| | | } finally { |
| | | // Release all locks that we took earlier |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | // Release all locks that we took earlier |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Computes the bucket and lock number for a particular key. |
| | | /// </summary> |
| | | private void GetBucketAndLockNo ( |
| | | int hashcode, out int bucketNo, out int lockNo, int bucketCount) |
| | | { |
| | | bucketNo = (hashcode & 0x7fffffff) % bucketCount; |
| | | lockNo = bucketNo % m_locks.Length; |
| | | /// <summary> |
| | | /// Computes the bucket and lock number for a particular key. |
| | | /// </summary> |
| | | private void GetBucketAndLockNo( |
| | | int hashcode, out int bucketNo, out int lockNo, int bucketCount) |
| | | { |
| | | bucketNo = (hashcode & 0x7fffffff) % bucketCount; |
| | | lockNo = bucketNo % m_locks.Length; |
| | | |
| | | Assert (bucketNo >= 0 && bucketNo < bucketCount); |
| | | Assert (lockNo >= 0 && lockNo < m_locks.Length); |
| | | } |
| | | Assert(bucketNo >= 0 && bucketNo < bucketCount); |
| | | Assert(lockNo >= 0 && lockNo < m_locks.Length); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// The number of concurrent writes for which to optimize by default. |
| | | /// </summary> |
| | | private static int DefaultConcurrencyLevel { |
| | | /// <summary> |
| | | /// The number of concurrent writes for which to optimize by default. |
| | | /// </summary> |
| | | private static int DefaultConcurrencyLevel |
| | | { |
| | | |
| | | get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; } |
| | | } |
| | | get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Acquires all locks for this hash table, and increments locksAcquired by the number |
| | | /// of locks that were successfully acquired. The locks are acquired in an increasing |
| | | /// order. |
| | | /// </summary> |
| | | private void AcquireAllLocks (ref int locksAcquired) |
| | | { |
| | | AcquireLocks (0, m_locks.Length, ref locksAcquired); |
| | | Assert (locksAcquired == m_locks.Length); |
| | | } |
| | | /// <summary> |
| | | /// Acquires all locks for this hash table, and increments locksAcquired by the number |
| | | /// of locks that were successfully acquired. The locks are acquired in an increasing |
| | | /// order. |
| | | /// </summary> |
| | | private void AcquireAllLocks(ref int locksAcquired) |
| | | { |
| | | AcquireLocks(0, m_locks.Length, ref locksAcquired); |
| | | Assert(locksAcquired == m_locks.Length); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Acquires a contiguous range of locks for this hash table, and increments locksAcquired |
| | | /// by the number of locks that were successfully acquired. The locks are acquired in an |
| | | /// increasing order. |
| | | /// </summary> |
| | | private void AcquireLocks (int fromInclusive, int toExclusive, ref int locksAcquired) |
| | | { |
| | | Assert (fromInclusive <= toExclusive); |
| | | /// <summary> |
| | | /// Acquires a contiguous range of locks for this hash table, and increments locksAcquired |
| | | /// by the number of locks that were successfully acquired. The locks are acquired in an |
| | | /// increasing order. |
| | | /// </summary> |
| | | private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired) |
| | | { |
| | | Assert(fromInclusive <= toExclusive); |
| | | |
| | | for (int i = fromInclusive; i < toExclusive; i++) { |
| | | bool lockTaken = false; |
| | | try { |
| | | Monitor.Enter (m_locks [i]); |
| | | lockTaken = true; |
| | | } finally { |
| | | if (lockTaken) { |
| | | locksAcquired++; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | for (int i = fromInclusive; i < toExclusive; i++) |
| | | { |
| | | bool lockTaken = false; |
| | | try |
| | | { |
| | | Monitor.Enter(m_locks[i]); |
| | | lockTaken = true; |
| | | } |
| | | finally |
| | | { |
| | | if (lockTaken) |
| | | { |
| | | locksAcquired++; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Releases a contiguous range of locks. |
| | | /// </summary> |
| | | private void ReleaseLocks (int fromInclusive, int toExclusive) |
| | | { |
| | | Assert (fromInclusive <= toExclusive); |
| | | /// <summary> |
| | | /// Releases a contiguous range of locks. |
| | | /// </summary> |
| | | private void ReleaseLocks(int fromInclusive, int toExclusive) |
| | | { |
| | | Assert(fromInclusive <= toExclusive); |
| | | |
| | | for (int i = fromInclusive; i < toExclusive; i++) { |
| | | Monitor.Exit (m_locks [i]); |
| | | } |
| | | } |
| | | for (int i = fromInclusive; i < toExclusive; i++) |
| | | { |
| | | Monitor.Exit(m_locks[i]); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a collection containing the keys in the dictionary. |
| | | /// </summary> |
| | | private ReadOnlyCollection<TKey> GetKeys () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | List<TKey> keys = new List<TKey> (); |
| | | /// <summary> |
| | | /// Gets a collection containing the keys in the dictionary. |
| | | /// </summary> |
| | | private ReadOnlyCollection<TKey> GetKeys() |
| | | { |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | List<TKey> keys = new List<TKey>(); |
| | | |
| | | for (int i = 0; i < m_buckets.Length; i++) { |
| | | Node current = m_buckets [i]; |
| | | while (current != null) { |
| | | keys.Add (current.m_key); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | for (int i = 0; i < m_buckets.Length; i++) |
| | | { |
| | | Node current = m_buckets[i]; |
| | | while (current != null) |
| | | { |
| | | keys.Add(current.m_key); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | |
| | | return new ReadOnlyCollection<TKey> (keys); |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | return new ReadOnlyCollection<TKey>(keys); |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Gets a collection containing the values in the dictionary. |
| | | /// </summary> |
| | | private ReadOnlyCollection<TValue> GetValues () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | List<TValue> values = new List<TValue> (); |
| | | /// <summary> |
| | | /// Gets a collection containing the values in the dictionary. |
| | | /// </summary> |
| | | private ReadOnlyCollection<TValue> GetValues() |
| | | { |
| | | int locksAcquired = 0; |
| | | try |
| | | { |
| | | AcquireAllLocks(ref locksAcquired); |
| | | List<TValue> values = new List<TValue>(); |
| | | |
| | | for (int i = 0; i < m_buckets.Length; i++) { |
| | | Node current = m_buckets [i]; |
| | | while (current != null) { |
| | | values.Add (current.m_value); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | for (int i = 0; i < m_buckets.Length; i++) |
| | | { |
| | | Node current = m_buckets[i]; |
| | | while (current != null) |
| | | { |
| | | values.Add(current.m_value); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | |
| | | return new ReadOnlyCollection<TValue> (values); |
| | | } finally { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | | return new ReadOnlyCollection<TValue>(values); |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks(0, locksAcquired); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// A helper method for asserts. |
| | | /// </summary> |
| | | [Conditional ("DEBUG")] |
| | | private void Assert (bool condition) |
| | | { |
| | | if (!condition) { |
| | | throw new Exception ("Assertion failed."); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// A helper method for asserts. |
| | | /// </summary> |
| | | [Conditional("DEBUG")] |
| | | private void Assert(bool condition) |
| | | { |
| | | if (!condition) |
| | | { |
| | | throw new Exception("Assertion failed."); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// A helper function to obtain the string for a particular resource key. |
| | | /// </summary> |
| | | /// <param name="key"></param> |
| | | /// <returns></returns> |
| | | private string GetResource (string key) |
| | | { |
| | | Assert (key != null); |
| | | /// <summary> |
| | | /// A helper function to obtain the string for a particular resource key. |
| | | /// </summary> |
| | | /// <param name="key"></param> |
| | | /// <returns></returns> |
| | | private string GetResource(string key) |
| | | { |
| | | Assert(key != null); |
| | | |
| | | return key; |
| | | } |
| | | return key; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// A node in a singly-linked list representing a particular hash table bucket. |
| | | /// </summary> |
| | | private class Node |
| | | { |
| | | internal TKey m_key; |
| | | internal TValue m_value; |
| | | internal volatile Node m_next; |
| | | internal int m_hashcode; |
| | | /// <summary> |
| | | /// A node in a singly-linked list representing a particular hash table bucket. |
| | | /// </summary> |
| | | private class Node |
| | | { |
| | | internal TKey m_key; |
| | | internal TValue m_value; |
| | | internal volatile Node m_next; |
| | | internal int m_hashcode; |
| | | |
| | | internal Node (TKey key, TValue value, int hashcode) |
| | | : this (key, value, hashcode, null) |
| | | { |
| | | } |
| | | internal Node(TKey key, TValue value, int hashcode) |
| | | : this(key, value, hashcode, null) |
| | | { |
| | | } |
| | | |
| | | internal Node (TKey key, TValue value, int hashcode, Node next) |
| | | { |
| | | m_key = key; |
| | | m_value = value; |
| | | m_next = next; |
| | | m_hashcode = hashcode; |
| | | } |
| | | } |
| | | internal Node(TKey key, TValue value, int hashcode, Node next) |
| | | { |
| | | m_key = key; |
| | | m_value = value; |
| | | m_next = next; |
| | | m_hashcode = hashcode; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// A private class to represent enumeration over the dictionary that implements the |
| | | /// IDictionaryEnumerator interface. |
| | | /// </summary> |
| | | private class DictionaryEnumerator : IDictionaryEnumerator |
| | | { |
| | | IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator; |
| | | // Enumerator over the dictionary. |
| | | /// <summary> |
| | | /// A private class to represent enumeration over the dictionary that implements the |
| | | /// IDictionaryEnumerator interface. |
| | | /// </summary> |
| | | private class DictionaryEnumerator : IDictionaryEnumerator |
| | | { |
| | | IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator; |
| | | // Enumerator over the dictionary. |
| | | |
| | | internal DictionaryEnumerator (ConcurrentDictionary<TKey, TValue> dictionary) |
| | | { |
| | | m_enumerator = dictionary.GetEnumerator (); |
| | | } |
| | | internal DictionaryEnumerator(ConcurrentDictionary<TKey, TValue> dictionary) |
| | | { |
| | | m_enumerator = dictionary.GetEnumerator(); |
| | | } |
| | | |
| | | public DictionaryEntry Entry { |
| | | get { return new DictionaryEntry (m_enumerator.Current.Key, m_enumerator.Current.Value); } |
| | | } |
| | | public DictionaryEntry Entry |
| | | { |
| | | get { return new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); } |
| | | } |
| | | |
| | | public object Key { |
| | | get { return m_enumerator.Current.Key; } |
| | | } |
| | | public object Key |
| | | { |
| | | get { return m_enumerator.Current.Key; } |
| | | } |
| | | |
| | | public object Value { |
| | | get { return m_enumerator.Current.Value; } |
| | | } |
| | | public object Value |
| | | { |
| | | get { return m_enumerator.Current.Value; } |
| | | } |
| | | |
| | | public object Current { |
| | | get { return this.Entry; } |
| | | } |
| | | public object Current |
| | | { |
| | | get { return this.Entry; } |
| | | } |
| | | |
| | | public bool MoveNext () |
| | | { |
| | | return m_enumerator.MoveNext (); |
| | | } |
| | | public bool MoveNext() |
| | | { |
| | | return m_enumerator.MoveNext(); |
| | | } |
| | | |
| | | public void Reset () |
| | | { |
| | | m_enumerator.Reset (); |
| | | } |
| | | } |
| | | public void Reset() |
| | | { |
| | | m_enumerator.Reset(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Get the data array to be serialized |
| | | /// </summary> |
| | | [OnSerializing] |
| | | private void OnSerializing (StreamingContext context) |
| | | { |
| | | // save the data into the serialization array to be saved |
| | | m_serializationArray = ToArray (); |
| | | m_serializationConcurrencyLevel = m_locks.Length; |
| | | m_serializationCapacity = m_buckets.Length; |
| | | } |
| | | /// <summary> |
| | | /// Get the data array to be serialized |
| | | /// </summary> |
| | | [OnSerializing] |
| | | private void OnSerializing(StreamingContext context) |
| | | { |
| | | // save the data into the serialization array to be saved |
| | | m_serializationArray = ToArray(); |
| | | m_serializationConcurrencyLevel = m_locks.Length; |
| | | m_serializationCapacity = m_buckets.Length; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Construct the dictionary from a previously seiralized one |
| | | /// </summary> |
| | | [OnDeserialized] |
| | | private void OnDeserialized (StreamingContext context) |
| | | { |
| | | KeyValuePair<TKey, TValue>[] array = m_serializationArray; |
| | | /// <summary> |
| | | /// Construct the dictionary from a previously seiralized one |
| | | /// </summary> |
| | | [OnDeserialized] |
| | | private void OnDeserialized(StreamingContext context) |
| | | { |
| | | KeyValuePair<TKey, TValue>[] array = m_serializationArray; |
| | | |
| | | m_buckets = new Node[m_serializationCapacity]; |
| | | m_countPerLock = new int[m_serializationConcurrencyLevel]; |
| | | m_buckets = new Node[m_serializationCapacity]; |
| | | m_countPerLock = new int[m_serializationConcurrencyLevel]; |
| | | |
| | | m_locks = new object[m_serializationConcurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | m_locks [i] = new object (); |
| | | } |
| | | m_locks = new object[m_serializationConcurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | m_locks[i] = new object(); |
| | | } |
| | | |
| | | InitializeFromCollection (array); |
| | | m_serializationArray = null; |
| | | InitializeFromCollection(array); |
| | | m_serializationArray = null; |
| | | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | #endif |