/// <summary> /// This extension method creates a new <see cref="ConcurrentQueue{T}"/> and possibly concurrently enumerates this <see cref="IAsyncEnumerable{T}"/>, adding each encountered item to the <see cref="ConcurrentQueue{T}"/>. /// </summary> /// <typeparam name="T">The type of items being enumerated.</typeparam> /// <param name="enumerable">This <see cref="IAsyncEnumerable{T}"/>.</param> /// <returns>A new <see cref="ConcurrentQueue{T}"/> holding all items encountered while enumerating this <see cref="IAsyncEnumerable{T}"/>.</returns> /// <exception cref="NullReferenceException">If this <see cref="IAsyncEnumerable{T}"/> is <c>null</c>.</exception> public static async Task <ConcurrentQueue <T> > ToConcurrentQueueAsync <T>(this IAsyncEnumerable <T> enumerable) { var retVal = new ConcurrentQueue <T>(); await enumerable.AddToConcurrentCollectionAsync(retVal, (queue, item) => queue.Enqueue(item)); return(retVal); }
/// <summary> /// This extension method creates a new <see cref="ConcurrentStack{T}"/> and possibly concurrently enumerates this <see cref="IAsyncEnumerable{T}"/>, adding each encountered item to the <see cref="ConcurrentStack{T}"/>. /// </summary> /// <typeparam name="T">The type of items being enumerated.</typeparam> /// <param name="enumerable">This <see cref="IAsyncEnumerable{T}"/>.</param> /// <returns>A new <see cref="ConcurrentStack{T}"/> holding all items encountered while enumerating this <see cref="IAsyncEnumerable{T}"/>.</returns> /// <exception cref="NullReferenceException">If this <see cref="IAsyncEnumerable{T}"/> is <c>null</c>.</exception> public static async Task <ConcurrentStack <T> > ToConcurrentStackAsync <T>(this IAsyncEnumerable <T> enumerable) { var retVal = new ConcurrentStack <T>(); await enumerable.AddToConcurrentCollectionAsync(retVal, (stack, item) => stack.Push(item)); return(retVal); }
/// <summary> /// This extension method creates a new <see cref="ConcurrentBag{T}"/> and possibly concurrently enumerates this <see cref="IAsyncEnumerable{T}"/>, adding each encountered item to the <see cref="ConcurrentBag{T}"/>. /// </summary> /// <typeparam name="T">The type of items being enumerated.</typeparam> /// <param name="enumerable">This <see cref="IAsyncEnumerable{T}"/>.</param> /// <returns>A new <see cref="ConcurrentBag{T}"/> holding all items encountered while enumerating this <see cref="IAsyncEnumerable{T}"/>.</returns> /// <exception cref="NullReferenceException">If this <see cref="IAsyncEnumerable{T}"/> is <c>null</c>.</exception> public static async Task <ConcurrentBag <T> > ToConcurrentBagAsync <T>(this IAsyncEnumerable <T> enumerable) { var retVal = new ConcurrentBag <T>(); await enumerable.AddToConcurrentCollectionAsync(retVal, (bag, item) => bag.Add(item)); return(retVal); }
/// <summary> /// This extension method creates a new <see cref="ConcurrentStack{T}"/> and possibly concurrently enumerates this <see cref="IAsyncEnumerable{T}"/>, adding each encountered item transformed by given <paramref name="selector"/> to the <see cref="ConcurrentStack{T}"/>. /// </summary> /// <typeparam name="T">The type of items being enumerated.</typeparam> /// <typeparam name="U">The type of items added to <see cref="ConcurrentStack{T}"/>.</typeparam> /// <param name="enumerable">This <see cref="IAsyncEnumerable{T}"/>.</param> /// <param name="selector">The callback to asynchronously select an object to be added to <see cref="ConcurrentStack{T}"/>.</param> /// <returns>A new <see cref="ConcurrentStack{T}"/> holding all transformed items encountered while enumerating this <see cref="IAsyncEnumerable{T}"/>.</returns> /// <remarks> /// <para> /// The motivation for this method is that often the items enumerated by <see cref="IAsyncEnumerable{T}"/> are "incomplete" in a sense that they require additional asynchronous processing (e.g. reading SQL row values, or reading the content of HTTP response). /// Using <see cref="Select{T, U}(IAsyncEnumerable{T}, Func{T, ValueTask{U}})"/> method will force the <see cref="IAsyncEnumerable{T}"/> into sequential enumerable, which may be undesired. /// Therefore, using this method directly it is possible to enumerate this <see cref="IAsyncEnumerable{T}"/> possibly concurrently into <see cref="ConcurrentStack{T}"/> while transforming each enumerable item into other type. /// </para> /// </remarks> public static async Task <ConcurrentStack <U> > ToConcurrentStackAsync <T, U>(this IAsyncEnumerable <T> enumerable, Func <T, Task <U> > selector) { ArgumentValidator.ValidateNotNull(nameof(selector), selector); var retVal = new ConcurrentStack <U>(); await enumerable.AddToConcurrentCollectionAsync(retVal, async ( stack, item ) => stack.Push(await selector(item))); return(retVal); }
/// <summary> /// This extension method will possibly concurrently enumerate this <see cref="IAsyncEnumerable{T}"/> into a <see cref="ConcurrentDictionary{TKey, TValue}"/>. /// </summary> /// <typeparam name="T">The type of items being enumerated.</typeparam> /// <typeparam name="TKey">The type of dictionary keys.</typeparam> /// <typeparam name="TValue">The type of dictionary values.</typeparam> /// <param name="enumerable">This <see cref="IAsyncEnumerable{T}"/>.</param> /// <param name="keySelector">The callback to potentially asynchronously create a dictionary key from enumerable item.</param> /// <param name="valueSelector">The callback to potentially asynchronously create a dictionary value from enumerable item.</param> /// <param name="equalityComparer">The optional <see cref="IEqualityComparer{T}"/> to use when creating dictionary.</param> /// <returns>Asynchronously returns a <see cref="IDictionary{TKey, TValue}"/> containing keys and values as returned by <paramref name="keySelector"/> and <paramref name="valueSelector"/>.</returns> /// <remarks> /// <para> /// TODO currently this will not throw if there are duplicate keys, unlike <see cref="ToDictionaryAsync{T, TKey, TValue}(IAsyncEnumerable{T}, Func{T, TKey}, Func{T, TValue}, IEqualityComparer{TKey})"/> method. /// The behaviour needs to be unified/parametrized at some point. /// </para> /// </remarks> /// <exception cref="NullReferenceException">If this <see cref="IAsyncEnumerable{T}"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">If either of <paramref name="keySelector"/> or <paramref name="valueSelector"/> is <c>null</c>.</exception> public static async Task <ConcurrentDictionary <TKey, TValue> > ToConcurrentDictionaryAsync <T, TKey, TValue>( this IAsyncEnumerable <T> enumerable, Func <T, ValueTask <TKey> > keySelector, Func <T, ValueTask <TValue> > valueSelector, IEqualityComparer <TKey> equalityComparer = null ) { ArgumentValidator.ValidateNotNullReference(enumerable); ArgumentValidator.ValidateNotNull(nameof(keySelector), keySelector); ArgumentValidator.ValidateNotNull(nameof(valueSelector), valueSelector); // Normal Dictionary<TKey, TValue> constructor accepts null as equality comparer, but ConcurrentDictionary throws... sigh. :) var retVal = new ConcurrentDictionary <TKey, TValue>(equalityComparer ?? EqualityComparer <TKey> .Default); await enumerable.AddToConcurrentCollectionAsync(retVal, async ( dictionary, item ) => dictionary.TryAdd(await keySelector(item), await valueSelector(item))); return(retVal); }