12 votes

Comment hydrater un dictionnaire avec les résultats d'appels asynchrones ?

Supposons que j'aie un code qui ressemble à ceci :

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

Supposons que je veuille créer un dictionnaire qui contienne le résultat de l'appel à DoSomethingReturnString pour chaque nombre similaire à celui-ci :

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

Cela ne fonctionnera pas car DoSomethingReturnString renvoie Task<string> plutôt que string . L'intellisense m'a suggéré d'essayer de spécifier mon expression lambda pour qu'elle soit asynchrone, mais cela n'a pas semblé résoudre le problème non plus.

6voto

shay__ Points 979

Si vous insistez pour le faire avec linq, Task.WhenAll est la clé pour "hydrater" le dictionnaire :

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

3voto

Yacoub Massad Points 16759

Les méthodes LINQ ne prennent pas en charge les actions asynchrones (par exemple, les sélecteurs de valeur asynchrones), mais vous pouvez en créer une vous-même. Voici un modèle réutilisable de ToDictionaryAsync qui prend en charge un sélecteur de valeur asynchrone :

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

Vous pouvez l'utiliser comme suit :

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

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

2voto

David L Points 22922

En cas d'appel à partir d'une méthode asynchrone, vous pouvez écrire une méthode enveloppante qui crée un nouveau dictionnaire et construit un dictionnaire en itérant sur chaque nombre, en appelant votre méthode DoSomethingReturnString à tour de rôle :

public async Task CallerAsync()
{
    int[] numbers = new int[] { 1, 2, 3 };
    Dictionary<int, string> dictionary = await ConvertToDictionaryAsync(numbers);
}

public async Task<Dictionary<int, string>> ConvertToDictionaryAsync(int[] numbers)
{
    var dict = new Dictionary<int, string>();

    for (int i = 0; i < numbers.Length; i++)
    {
        var n = numbers[i];
        dict[n] = await DoSomethingReturnString(n);
    }

    return dict;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X