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

时间:2015-06-15 18:30:43

标签: c# .net async-await task-parallel-library

以下面的界面为例:

interface IOracle
{
    Task<string> GetAnswerAsync(string question);
}

此界面的某些实现可能会使用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;
    }
}

编译器警告CS1998当然有道理。通常的建议是remove the async keyword and use Task.FromResult,但它错过了一个微妙的问题。如果代码抛出异常怎么办?然后,该代码转换会更改方法的行为:async版本将在Task中包装任何异常;非async版本不会,没有明确try - catch

async关键字完全按照我的意愿运行,但它会产生编译器警告,我不认为压制它们是明智的。

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

2 个答案:

答案 0 :(得分:6)

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

  • 删除async关键字。
  • 使用try - catch
  • 包裹整个现有方法正文
  • TaskCompletionSource<T> - tcs之前定义名为try的{​​{1}}。
  • catch的所有现有实例替换为return <expr>;,后跟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;
    }
}

答案 1 :(得分:5)

如果您使用的是.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);
    }
}

如果您使用的是.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;
}