Suppose I have code that looks like this:
public async Task<string> DoSomethingReturnString(int n) { ... } int[] numbers = new int[] { 1, 2 , 3};
Suppose that I want to create a dictionary that contains the result of calling DoSomethingReturnString
for each number similar to this:
Dictionary<int, string> dictionary = numbers.ToDictionary(n => n, n => DoSomethingReturnString(n));
That won't work because DoSomethingReturnString returns Task<string>
rather than string
. The intellisense suggested that I try specifying my lambda expression to be async, but this didn't seem to fix the problem either.
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
C# Language Async-Await Async/await will only improve performance if it allows the machine to do additional work.
Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
If you insist on doing it with linq, Task.WhenAll
is the key to "hydrate" the dictionary:
int[] numbers = new int[] { 1, 2 , 3}; KeyValuePair<int, string>[] keyValArray = //using KeyValuePair<,> to avoid GC pressure await Task.WhenAll(numbers.Select(async p => new KeyValuePair<int, string>(p, await DoSomethingReturnString(p)))); Dictionary<int, string> dict = keyValArray.ToDictionary(p => p.Key, p => p.Value);
LINQ methods do not support asynchronous actions (e.g., asynchronous value selectors), but you can create one yourself. Here is a reusable ToDictionaryAsync
extension method that supports an asynchronous value selector:
public static class ExtensionMethods { public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TInput, TKey, TValue>( this IEnumerable<TInput> enumerable, Func<TInput, TKey> syncKeySelector, Func<TInput, Task<TValue>> asyncValueSelector) { Dictionary<TKey,TValue> dictionary = new Dictionary<TKey, TValue>(); foreach (var item in enumerable) { var key = syncKeySelector(item); var value = await asyncValueSelector(item); dictionary.Add(key,value); } return dictionary; } }
You can use it like this:
private static async Task<Dictionary<int,string>> DoIt() { int[] numbers = new int[] { 1, 2, 3 }; return await numbers.ToDictionaryAsync( x => x, x => DoSomethingReturnString(x)); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With