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

本文关键字:同步 实现 任务 返回 CS1998 方法 情况下 警告 | 更新日期: 2023-09-27 17:56:15

以以下接口为例:

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 当然是有道理的。通常的建议是删除 async 关键字并使用 Task.FromResult ,但它错过了一个微妙的问题。 如果代码引发异常怎么办?然后,该代码转换会更改方法的行为:async版本会将任何异常包装在Task中;非async版本不会,没有明确的try - catch.

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

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

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

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

  • 删除async关键字。
  • try - catch 包装整个现有方法主体。
  • try - catch 之前定义一个名为 tcsTaskCompletionSource<T>
  • 将所有现有的 return <expr>; 实例替换为后跟 return tcs.Task; tcs.SetResult(<expr>); 的 。
  • 定义要调用tcs.SetException(e) catch块,后跟 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;
    }
}

这可以通过以下内容更普遍地表达,尽管我不知道将这样的帮助程序方法实际引入代码库是否合适。

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;
}

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