|
| 1 | +using System; |
| 2 | +using System.Collections.Concurrent; |
| 3 | +using System.Collections.Generic; |
| 4 | + |
| 5 | +namespace BitFaster.Caching.Atomic |
| 6 | +{ |
| 7 | + /// <summary> |
| 8 | + /// Convenience methods for using AtomicFactory with ConcurrentDictionary. |
| 9 | + /// </summary> |
| 10 | + public static class ConcurrentDictionaryExtensions |
| 11 | + { |
| 12 | + /// <summary> |
| 13 | + /// Adds a key/value pair to the ConcurrentDictionary if the key does not already exist. Returns the new value, or the existing value if the key already exists. |
| 14 | + /// </summary> |
| 15 | + /// <param name="dictionary">The ConcurrentDictionary to use.</param> |
| 16 | + /// <param name="key">The key of the element to add.</param> |
| 17 | + /// <param name="valueFactory">The function used to generate a value for the key.</param> |
| 18 | + /// <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> |
| 19 | + public static V GetOrAdd<K, V>(this ConcurrentDictionary<K, AtomicFactory<K, V>> dictionary, K key, Func<K, V> valueFactory) |
| 20 | + { |
| 21 | + var atomicFactory = dictionary.GetOrAdd(key, _ => new AtomicFactory<K, V>()); |
| 22 | + return atomicFactory.GetValue(key, valueFactory); |
| 23 | + } |
| 24 | + |
| 25 | + /// <summary> |
| 26 | + /// Adds a key/value pair to the ConcurrentDictionary by using the specified function and an argument if the key does not already exist, or returns the existing value if the key exists. |
| 27 | + /// </summary> |
| 28 | + /// <param name="dictionary">The ConcurrentDictionary to use.</param> |
| 29 | + /// <param name="key">The key of the element to add.</param> |
| 30 | + /// <param name="valueFactory">The function used to generate a value for the key.</param> |
| 31 | + /// <param name="factoryArgument">An argument value to pass into valueFactory.</param> |
| 32 | + /// <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> |
| 33 | + public static V GetOrAdd<K, V, TArg>(this ConcurrentDictionary<K, AtomicFactory<K, V>> dictionary, K key, Func<K, TArg, V> valueFactory, TArg factoryArgument) |
| 34 | + { |
| 35 | + var atomicFactory = dictionary.GetOrAdd(key, _ => new AtomicFactory<K, V>()); |
| 36 | + return atomicFactory.GetValue(key, valueFactory, factoryArgument); |
| 37 | + } |
| 38 | + |
| 39 | + /// <summary> |
| 40 | + /// Attempts to get the value associated with the specified key from the ConcurrentDictionary. |
| 41 | + /// </summary> |
| 42 | + /// <param name="dictionary">The ConcurrentDictionary to use.</param> |
| 43 | + /// <param name="key">The key of the value to get.</param> |
| 44 | + /// <param name="value">When this method returns, contains the object from the ConcurrentDictionary that has the specified key, or the default value of the type if the operation failed.</param> |
| 45 | + /// <returns>true if the key was found in the ConcurrentDictionary; otherwise, false.</returns> |
| 46 | + public static bool TryGetValue<K, V>(this ConcurrentDictionary<K, AtomicFactory<K, V>> dictionary, K key, out V value) |
| 47 | + { |
| 48 | + AtomicFactory<K, V> output; |
| 49 | + var ret = dictionary.TryGetValue(key, out output); |
| 50 | + |
| 51 | + if (ret && output.IsValueCreated) |
| 52 | + { |
| 53 | + value = output.ValueIfCreated; |
| 54 | + return true; |
| 55 | + } |
| 56 | + |
| 57 | + value = default; |
| 58 | + return false; |
| 59 | + } |
| 60 | + |
| 61 | + /// <summary> |
| 62 | + /// Removes a key and value from the dictionary. |
| 63 | + /// </summary> |
| 64 | + /// <param name="dictionary">The ConcurrentDictionary to use.</param> |
| 65 | + /// <param name="item">The KeyValuePair representing the key and value to remove.</param> |
| 66 | + /// <returns>true if the object was removed successfully; otherwise, false.</returns> |
| 67 | + public static bool TryRemove<K, V>(this ConcurrentDictionary<K, AtomicFactory<K, V>> dictionary, KeyValuePair<K, V> item) |
| 68 | + { |
| 69 | + var kvp = new KeyValuePair<K, AtomicFactory<K, V>>(item.Key, new AtomicFactory<K, V>(item.Value)); |
| 70 | +#if NET6_0_OR_GREATER |
| 71 | + return dictionary.TryRemove(kvp); |
| 72 | +#else |
| 73 | + // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/ |
| 74 | + return ((ICollection<KeyValuePair<K, AtomicFactory<K, V>>>)dictionary).Remove(kvp); |
| 75 | +#endif |
| 76 | + } |
| 77 | + |
| 78 | + /// <summary> |
| 79 | + /// Attempts to remove and return the value that has the specified key from the ConcurrentDictionary. |
| 80 | + /// </summary> |
| 81 | + /// <param name="dictionary">The ConcurrentDictionary to use.</param> |
| 82 | + /// <param name="key">The key of the element to remove and return.</param> |
| 83 | + /// <param name="value">When this method returns, contains the object removed from the ConcurrentDictionary, or the default value of the TValue type if key does not exist.</param> |
| 84 | + /// <returns>true if the object was removed successfully; otherwise, false.</returns> |
| 85 | + public static bool TryRemove<K, V>(this ConcurrentDictionary<K, AtomicFactory<K, V>> dictionary, K key, out V value) |
| 86 | + { |
| 87 | + if (dictionary.TryRemove(key, out var atomic)) |
| 88 | + { |
| 89 | + value = atomic.ValueIfCreated; |
| 90 | + return true; |
| 91 | + } |
| 92 | + |
| 93 | + value = default; |
| 94 | + return false; |
| 95 | + } |
| 96 | + } |
| 97 | +} |
0 commit comments