选择异步功能的更好方法?

Cor*_*ehn 5 .net c# linq select func

我一直在重构我的项目中的一个常见模式,发现它不像使用 LINQSelect到异步函数那么简单。

对于上下文,这是当前的完成方式。

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

Run Code Online (Sandbox Code Playgroud)

现在,如果我尝试ForEach使用 LINQ替换循环部分:

var executeQueries = domains.Select(domain =>
    async (ct) => await ExecuteQueryGroupsForDomain(domain, 123, ct));
Run Code Online (Sandbox Code Playgroud)

它抱怨Type arguments cannot be inferred by the usage让我相信我没有从 返回任何东西Select,但我显然正在返回Func我想要的。

有没有更好的方法来创建Func's列表,最好避免显式强制转换?当Select'd async 方法清楚地告诉它应该是什么类型时,还有什么解释为什么编译器无法推断类型吗?


需要明确的是,我确实需要将 the 传递CancellationTokenFunc's 因为它是与外部令牌不同的令牌(具体来说,它是将外部令牌与另一个内部令牌联系起来的链接令牌)。

Mar*_*sky 2

问题出在 select 的返回值上,因为编译器不清楚返回值的类型是什么。所以,你需要明确返回的类型,这里有两种方法:

executeQueries = domains.Select(domain => 
    new Func<CancellationToken, Task<ICollection<Group>>>(token => 
        this.ExecuteQueryGroupsForDomain(domain, 123, token))).ToList();

executeQueries = domains
    .Select<DomainInfo, Func<CancellationToken, Task<ICollection<Group>>>>(domain =>
        ct => this.ExecuteQueryGroupsForDomain(domain, 123, ct)).ToList();
Run Code Online (Sandbox Code Playgroud)

=================================================== ===================

编辑1:编译器无法从 lambda 表达式推断类型,因为 lambda 只是匿名方法的简写,而不是类型。因此,如果返回是基本委托或其他委托类型(例如 Action、Func 等),您需要明确指出方法的返回类型。查看其他答案,其中根据 C# 4 解释错误编译器规格

如果您需要将原始代码转换为更具可读性的内容,我认为没有其他更具可读性的方法。以下是编写代码的其他方式:

foreach (var domain in domains) {
    executeQueries.Add(token => this.ExecuteQueryGroupsForDomain(domain, 123, token));
}
executeQueries.AddRange(domains
    .Select(domain => (Func<CancellationToken, Task<ICollection<Group>>>) (token => 
        this.ExecuteQueryGroupsForDomain(domain, 123, token))));
executeQueries =
    (from domain in domains
    select new Func<CancellationToken, Task<ICollection<Group>>>(token => 
        this.ExecuteQueryGroupsForDomain(domain, 123, token))).ToList()
Run Code Online (Sandbox Code Playgroud)