如何在没有警告CS1998的情况下实现同步任务返回方法?

Tim*_*lds 10 .net c# task-parallel-library async-await

以下面的界面为例:

interface IOracle
{
    Task<string> GetAnswerAsync(string question);
}
Run Code Online (Sandbox Code Playgroud)

此接口的某些实现可能使用async/ await.其他人可能不需要.例如,考虑这个简单的玩具实现.

class SimpleOracle
{
    public Dictionary<string, string> Lookup { get; set; }

    // Warning CS1998: This async method lacks 'await' operators
    // and will run synchonously.
    public async Task<string> GetAnswerAsync(string question)
    {
        string answer = Lookup[question];
        return answer;
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器警告CS1998当然是有道理的.通常的建议是删除async关键字并使用Task.FromResult,但它错过了一个微妙的问题.如果代码抛出异常怎么办?然后该代码转换改变了方法的行为:async版本将包装任何异常Task; 非async版本不会,没有明确的try- catch.

async关键字完全按照我的意愿工作,但它会产生编译器警告,我不认为压制这些是明智的.

我应该如何重构我的方法实现以不产生编译器警告,同时用Task任何其他async方法包装所有异常?

Tim*_*lds 9

我用来将async产生编译器警告CS1998 的版本转换为async行为相同的非版本的机械转换如下.

  • 删除async关键字.
  • 包住整个现有方法机身采用了try- catch.
  • 在- 之前定义一个TaskCompletionSource<T>被调用者.tcstrycatch
  • 替换所有现有的return <expr>;with 实例tcs.SetResult(<expr>);后跟return tcs.Task;.
  • 定义catch要调用的块tcs.SetException(e)后跟return tcs.Task;.

例如:

public Task<string> GetAnswerAsync(string question)
{
    var tcs = new TaskCompletionSource<string>();
    try
    {
        string answer = Lookup[question];
        tcs.SetResult(answer);
        return tcs.Task;
    }
    catch (Exception e)
    {
        tcs.SetException(e);
        return tcs.Task;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以通过以下方式更一般地表达,尽管我不知道将这样的辅助方法实际引入代码库是否合适.

public static Task<T> AsyncPattern(Func<T> func)
{
    var tcs = new TaskCompletionSource<T>();
    try
    {
        tcs.SetResult(func());
    }
    catch (Exception e)
    {
        tcs.SetException(e);
    }
    return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 6

如果使用的是.NET 4.6,则可以Task.FromException用来处理异常情况,就像FromResult用来处理成功情况一样:

public Task<string> GetAnswerAsync(string question)
{
    try
    {
        return Task.FromResult(Lookup[question]);
    }
    catch(Exception e)
    {
        return Task.FromException<string>(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果使用的是.NET 4.5,则需要编写自己的FromException方法,但这很简单:

public static Task<T> FromException<T>(Exception e)
{
    var tcs = new TaskCompletionSource<T>();
    tcs.SetException(e);
    return tcs.Task;
}
public static Task FromException(Exception e)
{
    var tcs = new TaskCompletionSource<bool>();
    tcs.SetException(e);
    return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)