【问题标题】:How to implement synchronous Task-returning method without warning CS1998?CS1998如何实现同步任务返回方法而不提示CS1998?
【发布时间】:2015-06-15 18:30:42
【问题描述】:

以如下界面为例:

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 关键字完全符合我的要求,但它会产生编译器警告,我认为禁止这些是不明智的。

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 检查thisthis 以获得另一个选项:Task.RunSynchronously。我个人比TaskCompletionSource 方法更喜欢它。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      • 2019-08-27
      • 1970-01-01
      • 1970-01-01
      • 2013-04-26
      • 1970-01-01
      相关资源
      最近更新 更多