如何将异步方法放在列表中并以迭代方式调用它们



>最近我想对服务调用列表实施运行状况检查。它们都是异步任务(例如 Task<IHttpOperationResponse<XXX_Model>> method_name(...)

我想把所有这些放在一个列表中。我遵循了这篇文章的答案:在 C# 中存储方法列表 但是,它们是异步方法。

我是这样说的:异步方法的集合

List<Action> _functions = new List<Action> {
      () => accountDetailsServiceProvider.GetEmployer(EmployerId),                                  
      () => accountServiceProvider.GetAccountStatus(EmployerId)
}

有人可以指导我实现将异步方法放入列表并迭代调用它们的正确方法吗?

提前感谢!

首先,你需要使方法异步。这意味着他们必须返回任务。例如:

public static async Task Foo()
{
    await Task.Delay(1);
    Console.WriteLine("Foo!");
}
public static async Task Bar()
{
    await Task.Delay(1);
    Console.WriteLine("Bar!");
}

然后,若要将它们放入列表中,必须将列表定义为包含正确的类型。由于异步方法实际上返回了一些东西,因此它是一个 Func,而不是一个操作。它返回一个任务。

var actions = new List<Func<Task>>
{
    Foo, Bar
};

若要调用它们,请在列表上选择(使用 Linq)以调用它们。这将创建一个任务列表来代替函数列表。

var tasks = actions.Select( x => x() );

然后等待他们:

await Task.WhenAll(tasks);

完整示例:

public static async Task MainAsync()
{
    var actions = new List<Func<Task>>
    {
        Foo, Bar
    };
    var tasks = actions.Select( x => x() );
    await Task.WhenAll(tasks);
}

输出:

Foo!
Bar!

DotNetFiddle的例子

如果您的方法返回布尔值,则返回类型将变为Task<bool>,其余返回类型也随之而来:

public static async Task<bool> Foo()
{
    await Task.Delay(1);
    Console.WriteLine("Foo!");
    return true;
}
public static async Task<bool> Bar()
{
    await Task.Delay(1);
    Console.WriteLine("Bar!");
    return true;
}
var actions = new List<Func<Task<bool>>>
{
    Foo, Bar
};
var tasks = actions.Select( x => x() );
await Task.WhenAll(tasks);

等待任务后,可以使用另一个 LINQ 语句将任务转换为其结果:

List<bool> results = tasks.Select( task => task.Result ).ToList();

我想你只是在寻找这样简单的东西?

var myList = new List<Action>()
{
    async() => { await Foo.GetBarAsync(); },
    ...
};

我建议您像这样将类型从Action更改为Func<Task>

var myList = new List<Func<Task>>()
{
  async() => { await Foo.GetBarAsync(); },
};

您可以在此处阅读有关原因的更多信息:https://blogs.msdn.microsoft.com/pfxteam/2012/02/08/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/

调用(简体)

foreach (var action in myList)
{
    await action.Invoke();
}

根据评论:

但是,我的任务需要每个方法调用的布尔值, 因为我必须向前端报告状态是否 服务是否关闭

为将返回所需布尔值的方法创建一个包装方法

public async Task<Result> Check(string name, Func<Task> execute)
{
    try
    {
        await execute();
        return new Result(name, true, string.Empty);
    }
    catch (Exception ex)
    {
        return new Result(name, false, ex.Message);
    }
}
public class Result
{
    public string Name { get; }
    public bool Success { get; }
    public string Message { get; }
    public Result(string name, bool success, string message) 
        => (Name, Success, Message) = (name, success, message);
}

然后,您不需要收集委托,而是收集Task

var tasks = new[]
{
    Check(nameof(details.GetEmployer), () => details.GetEmployer(Id)),
    Check(nameof(accounts.GetAccountStatus), () => accounts.GetAccountStatus(Id)),
};
var completed = await Task.WhenAll(tasks);
foreach (var task in completed)
{
    Console.WriteLine($"Task: {task.Name}, Success: {task.Success};");
}

最新更新