私は自分のプロジェクトの一般的なパターンをリファクタリングしており、非同期関数に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非同期メソッドが型を明確に指示しているのに、コンパイラーが型を推測できない理由はありますか?
明確にするために、CancellationToken
をFunc
に渡す必要があります。これは、外部トークンとは別のトークンであるためです(具体的には、外部トークンを別の内部トークンに関連付けるリンクトークンです) )。
以下のような拡張メソッドを使用すると、読みやすさが向上する場合があります。 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>>>>
。
本当に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);