更好的选择到异步Func的方法?

问题描述 投票:0回答:1

我一直在重构我的项目中的一个常见模式,发现它并不像使用 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)));


现在,如果我尝试将 ForEach 循环部分使用LINQ。

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 async方法清楚地告诉它应该是什么类型?


为了清楚起见,我确实需要通过 CancellationTokenFunc'的原因是它是一个与外部标记不同的标记(具体来说,它是一个链接标记,将外部标记与另一个内部标记联系起来)。

c# .net linq select func
1个回答
2
投票

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

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

EDIT 1:编译器无法从lambda表达式中推断出类型,因为lambda只是一个匿名方法的简写,而不是类型。所以,你需要显式并指明方法的返回类型,如果返回的是base Delegate或其他委托类型,如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()

1
投票

你真的需要 Func's?

你可以使用下面的,如果实际的 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
投票

你可以通过使用像下面这样的扩展方法来获得一些可读性。它使用与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>>>>.

© www.soinside.com 2019 - 2024. All rights reserved.