1 //--------------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 //--------------------------------------------------------------------------
9 using System.Collections;
10 using System.Collections.Concurrent;
11 using System.Collections.Generic;
12 using System.Diagnostics;
15 using System.Threading.Tasks;
17 namespace System.Threading
19 /// <summary>Debugger type proxy for AsyncCache.</summary>
20 /// <typeparam name="TKey">Specifies the type of the cache's keys.</typeparam>
21 /// <typeparam name="TValue">Specifies the type of the cache's values.</typeparam>
22 internal class AsyncCache_DebugView<TKey, TValue>
24 private readonly AsyncCache<TKey, TValue> _asyncCache;
26 internal AsyncCache_DebugView(AsyncCache<TKey,TValue> asyncCache)
28 _asyncCache = asyncCache;
31 [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
32 internal KeyValuePair<TKey, Task<TValue>>[] Values
34 get { return _asyncCache.ToArray(); }
38 /// <summary>Caches asynchronously retrieved data.</summary>
39 /// <typeparam name="TKey">Specifies the type of the cache's keys.</typeparam>
40 /// <typeparam name="TValue">Specifies the type of the cache's values.</typeparam>
41 [DebuggerTypeProxy(typeof(AsyncCache_DebugView<,>))]
42 [DebuggerDisplay("Count={Count}")]
43 public class AsyncCache<TKey, TValue> : ICollection<KeyValuePair<TKey,Task<TValue>>>
45 /// <summary>The factory to use to create tasks.</summary>
46 private readonly Func<TKey, Task<TValue>> _valueFactory;
47 /// <summary>The dictionary to store all of the tasks.</summary>
48 private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;
50 /// <summary>Initializes the cache.</summary>
51 /// <param name="valueFactory">A factory for producing the cache's values.</param>
52 public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
54 if (valueFactory == null) throw new ArgumentNullException("loader");
55 _valueFactory = valueFactory;
56 _map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
59 /// <summary>Gets a Task to retrieve the value for the specified key.</summary>
60 /// <param name="key">The key whose value should be retrieved.</param>
61 /// <returns>A Task for the value of the specified key.</returns>
62 public Task<TValue> GetValue(TKey key)
64 if (key == null) throw new ArgumentNullException("key");
65 var value = new Lazy<Task<TValue>>(() => _valueFactory(key));
66 return _map.GetOrAdd(key, value).Value;
69 /// <summary>Sets the value for the specified key.</summary>
70 /// <param name="key">The key whose value should be set.</param>
71 /// <param name="value">The value to which the key should be set.</param>
72 public void SetValue(TKey key, TValue value)
74 SetValue(key, Task.Factory.FromResult(value));
77 /// <summary>Sets the value for the specified key.</summary>
78 /// <param name="key">The key whose value should be set.</param>
79 /// <param name="value">The value to which the key should be set.</param>
80 public void SetValue(TKey key, Task<TValue> value)
82 if (key == null) throw new ArgumentNullException("key");
83 _map[key] = LazyExtensions.Create(value);
86 /// <summary>Gets a Task to retrieve the value for the specified key.</summary>
87 /// <param name="key">The key whose value should be retrieved.</param>
88 /// <returns>A Task for the value of the specified key.</returns>
89 public Task<TValue> this[TKey key]
91 get { return GetValue(key); }
92 set { SetValue(key, value); }
95 /// <summary>Empties the cache.</summary>
96 public void Clear() { _map.Clear(); }
98 /// <summary>Gets the number of items in the cache.</summary>
99 public int Count { get { return _map.Count; } }
101 /// <summary>Gets an enumerator for the contents of the cache.</summary>
102 /// <returns>An enumerator for the contents of the cache.</returns>
103 public IEnumerator<KeyValuePair<TKey, Task<TValue>>> GetEnumerator()
105 return _map.Select(p => new KeyValuePair<TKey, Task<TValue>>(p.Key, p.Value.Value)).GetEnumerator();
108 /// <summary>Gets an enumerator for the contents of the cache.</summary>
109 /// <returns>An enumerator for the contents of the cache.</returns>
110 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
112 /// <summary>Adds or overwrites the specified entry in the cache.</summary>
113 /// <param name="item">The item to be added.</param>
114 void ICollection<KeyValuePair<TKey, Task<TValue>>>.Add(KeyValuePair<TKey, Task<TValue>> item)
116 this[item.Key] = item.Value;
119 /// <summary>Determines whether the cache contains the specified key.</summary>
120 /// <param name="item">The item contained the key to be searched for.</param>
121 /// <returns>True if the cache contains the key; otherwise, false.</returns>
122 bool ICollection<KeyValuePair<TKey, Task<TValue>>>.Contains(KeyValuePair<TKey, Task<TValue>> item)
124 return _map.ContainsKey(item.Key);
128 /// Copies the elements of the System.Collections.Generic.ICollection<T> to an
129 /// System.Array, starting at a particular System.Array index.
131 /// <param name="array">
132 /// The one-dimensional System.Array that is the destination of the elements
133 /// copied from System.Collections.Generic.ICollection<T>. The System.Array must
134 /// have zero-based indexing.
136 /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
137 void ICollection<KeyValuePair<TKey, Task<TValue>>>.CopyTo(KeyValuePair<TKey, Task<TValue>>[] array, int arrayIndex)
139 ((ICollection<KeyValuePair<TKey, Task<TValue>>>)_map).CopyTo(array, arrayIndex);
142 /// <summary>Gets whether the cache is read-only.</summary>
143 bool ICollection<KeyValuePair<TKey, Task<TValue>>>.IsReadOnly { get { return false; } }
145 /// <summary>Removes the specified key from the cache.</summary>
146 /// <param name="item">The item containing the key to be removed.</param>
147 /// <returns>True if the item could be removed; otherwise, false.</returns>
148 bool ICollection<KeyValuePair<TKey, Task<TValue>>>.Remove(KeyValuePair<TKey, Task<TValue>> item)
150 Lazy<Task<TValue>> value;
151 return _map.TryRemove(item.Key, out value);
155 /// <summary>An asynchronous cache for downloaded HTML.</summary>
156 public sealed class HtmlAsyncCache : AsyncCache<Uri, string>
158 /// <summary>Initializes the HtmlCache.</summary>
159 public HtmlAsyncCache() :
160 base(uri => new WebClient().DownloadStringTask(uri)) { }