如何使异步方法对任务类型通用

Jef*_*ger 3 c# generics asynchronous

我的问题从表面上看与使用泛型类型作为异步方法的返回类型非常相似。但是,他没有现实世界的使用场景,因此可以说的是您无法做到这一点。我从乔恩·斯凯特(Jon Skeet)对这个问题的回答以及博客文章中了解到,为什么异步方法必须返回Task?为什么不支持它。这篇文章讨论了解决方法,但是没有一个适用于我的用例。

我的问题是,鉴于我正在尝试做的事情,解决语言限制的最佳方法是什么?

在生产代码中,我们有一个retry方法,该方法采用a Func<Task>并返回Task。然后,我们还需要能够重试TaskT>功能。因此,我们将重试方法更改为(略有简化):

public static async T Retry<T>(Func<T> action) where T : Task
{
    for (var i = 0; ; i++)
    {
        try
        {
            return await action();
        }
        catch (Exception)
        {
            if (i >= 3)
                throw;
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器当然会报告错误“异步方法的返回类型必须为空,任务或任务”。我可以做Retry的工作既TaskTask<T>不写入方法的两个副本?

Jon*_*eet 5

不,异步只是不支持。伪造的最简单选择是:

public static async Task<T> Retry<T>(Func<Task<T>> action)
{
    for (var i = 0; ; i++)
    {
        try
        {
            return await action();
        }
        catch (Exception)
        {
            if (i >= 3)
                throw;
        }       
    }
}

public static async Task Retry(Func<Task> action)
{
    Func<Task<int>> d = async () => { await action(); return 0; };
    await Retry(d);
}
Run Code Online (Sandbox Code Playgroud)

这不涉及代码重复,但是当您调用非通用版本时,您确实会面临很多彼此重叠的任务层。您可以将后者更改为:

public static Task Retry(Func<Task> action)
{
    Func<Task<int>> d = async () => { await action(); return 0; };
    return Retry(d);
}
Run Code Online (Sandbox Code Playgroud)

然后,它实际上会返回一个Task<int>,但是假设您的呼叫者不在乎,它确实节省了一部分重定向。