web-dev-qa-db-ja.com

非同期関数を選択するより良い方法は?

私は自分のプロジェクトの一般的なパターンをリファクタリングしており、非同期関数にLINQ Selectを使用するほど簡単ではないことがわかりました。

コンテキストについては、次のとおりです。

async Task<ICollection<Group>> ExecuteQueryGroupsForDomain(DomainInfo domain, int batchSize, CancellationToken ct)
{
    try
    {
        return await BlahBlahActuallyGoGetGroupsForDomainHere(domain, batchSize, ct);
    }
    catch (Exception e)
    {
        return null;
    }
}

var executeQueries = new List<Func<CancellationToken, Task<ICollection<Group>>>>();

domains.ForEach(domain =>
    executeQueries.Add(async (ct) =>
        await ExecuteQueryGroupsForDomain(domain, 123, ct)));


LINQを使用してForEachループセクションを置き換えようとすると、次のようになります。

var executeQueries = domains.Select(domain =>
    async (ct) => await ExecuteQueryGroupsForDomain(domain, 123, ct));

文句を言うType arguments cannot be inferred by the usageこれは私がSelectから何も返していないと信じるように導きますが、私は明らかにFuncを返しています。

Funcのリストを作成するより良い方法はありますか?理想的には明示的なキャストを避けますか?また、Select 'd非同期メソッドが型を明確に指示しているのに、コンパイラーが型を推測できない理由はありますか?


明確にするために、CancellationTokenFuncに渡す必要があります。これは、外部トークンとは別のトークンであるためです(具体的には、外部トークンを別の内部トークンに関連付けるリンクトークンです) )。

5
Cord Rehn

以下のような拡張メソッドを使用すると、読みやすさが向上する場合があります。 LINQ Select メソッドでも同じ引数を使用しますが、具体化されたタスクではなくタスクファクトリを返します。

public static IEnumerable<Func<CancellationToken, Task<TResult>>> SelectTaskFactory
    <TSource, TResult>(this IEnumerable<TSource> source,
    Func<TSource, CancellationToken, Task<TResult>> selector)
{
    return source.Select(item =>
    {
        return new Func<CancellationToken, Task<TResult>>(ct => selector(item, ct));
    });
}

使用例:

var executeQueries = domains.SelectTaskFactory(async (domain, ct) =>
{
    return await ExecuteQueryGroupsForDomain(domain, 123, ct);
}).ToList();

executeQueries変数のタイプはList<Func<CancellationToken, Task<ICollection<Group>>>>

1
Theodor Zoulias

本当にFuncが必要ですか?

実際のCancellationTokenがすでに存在する場合は、以下を使用できます。

// create and start a Task for each domain
var executeQueryTasks = domains.Select(domain => ExecuteQueryGroupsForDomain(domain, 123, ct));

// wait until all tasks are finished and get the result in an array
var executedQueries = await Task.WhenAll(executeQueryTasks);
1
user8810910