如何使用异步调用的结果来水合词典?

时间:2016-06-13 17:49:56

标签: c# async-await

假设我的代码如下所示:

public async Task<string> DoSomethingReturnString(int n) { ... }
int[] numbers = new int[] { 1, 2 , 3};

假设我想创建一个字典,其中包含为与此类似的每个数字调用DoSomethingReturnString的结果:

Dictionary<int, string> dictionary = numbers.ToDictionary(n => n,
    n => DoSomethingReturnString(n));

这不起作用,因为DoSomethingReturnString返回Task<string>而不是string。 intellisense建议我尝试将我的lambda表达式指定为异步,但这似乎也没有解决问题。

4 个答案:

答案 0 :(得分:6)

如果你坚持用linq做,Task.WhenAll是“保湿”词典的关键:

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);

答案 1 :(得分:5)

LINQ方法不支持异步操作(例如,异步值选择器),但您可以自己创建一个。这是一个可重用的ToDictionaryAsync扩展方法,它支持异步值选择器:

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;
    }
}

你可以像这样使用它:

private static async Task<Dictionary<int,string>>  DoIt()
{
    int[] numbers = new int[] { 1, 2, 3 };

    return await numbers.ToDictionaryAsync(
        x => x,
        x => DoSomethingReturnString(x));
}

答案 2 :(得分:3)

如果从异步方法调用,您可以编写一个包装器方法,通过迭代每个数字来创建一个新字典并构建一个字典,依次调用Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", "" Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c" Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c" Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c" Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"

DoSomethingReturnString

答案 3 :(得分:0)

这只是@Yacoub和@David对于使用Task.WhenAll的扩展方法的答案的组合

public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TInput, TKey, TValue>(
    this IEnumerable<TInput> enumerable,
    Func<TInput, TKey> syncKeySelector,
    Func<TInput, Task<TValue>> asyncValueSelector)
{
    KeyValuePair<TKey, TValue>[] keyValuePairs = await Task.WhenAll(
        enumerable.Select(async input => new KeyValuePair<TKey, TValue>(syncKeySelector(input), await asyncValueSelector(input)))
    );
    return keyValuePairs.ToDictionary(pair => pair.Key, pair => pair.Value);
}