【问题标题】:Suppress warning CS1998: This async method lacks 'await'禁止警告 CS1998:此异步方法缺少“等待”
【发布时间】:2012-10-25 23:44:40
【问题描述】:

我有一个接口,其中包含一些返回Task 的函数。一些实现接口的类没有任何等待,而另一些则可能只是抛出 - 所以警告是虚假和烦人的。

是否可以抑制这些警告?例如:

public async Task<object> test()
{
    throw new NotImplementedException();
}

产量:

警告 CS1998:此异步方法缺少“等待”运算符,将运行 同步。考虑使用 'await' 运算符来等待 非阻塞 API 调用,或 'await Task.Run(...)' 来做 CPU 密集型工作 在后台线程上。

【问题讨论】:

标签: c# asynchronous


【解决方案1】:

我有一个带有一些异步功能的接口。

我相信返回 Task 的方法。 async 是一个实现细节,所以不能应用于接口方法。

一些实现接口的类没有任何等待,有些可能只是抛出。

在这些情况下,您可以利用async 是一个实现细节这一事实。

如果你对await没有任何意见,那么你可以直接返回Task.FromResult

public Task<int> Success() // note: no "async"
{
  ... // non-awaiting code
  int result = ...;
  return Task.FromResult(result);
}

在抛出NotImplementedException的情况下,程序就有点罗嗦了:

public Task<int> Fail() // note: no "async"
{
  var tcs = new TaskCompletionSource<int>();
  tcs.SetException(new NotImplementedException());
  return tcs.Task;
}

如果你有很多方法抛出NotImplementedException(这本身可能表明一些设计级别的重构会很好),那么你可以将冗长的内容包装到一个帮助类中:

public static class TaskConstants<TResult>
{
  static TaskConstants()
  {
    var tcs = new TaskCompletionSource<TResult>();
    tcs.SetException(new NotImplementedException());
    NotImplemented = tcs.Task;
  }

  public static Task<TResult> NotImplemented { get; private set; }
}

public Task<int> Fail() // note: no "async"
{
  return TaskConstants<int>.NotImplemented;
}

帮助类还减少了 GC 否则必须收集的垃圾,因为具有相同返回类型的每个方法都可以共享其 TaskNotImplementedException 对象。

我还有其他几个"task constant" type examples in my AsyncEx library

【讨论】:

  • 我没想到会丢失关键字。正如您所说,异步与接口无关。我的错,谢谢。
  • 你能推荐一种返回类型只是任务(没有结果吗?)的方法
  • 警告:这种方法可能会导致问题,因为错误不会以您期望的方式传播。通常,调用者会期望您的方法中的异常会出现在Task 中。相反,您的方法将在它有机会创建Task 之前抛出。我真的认为最好的模式是定义一个没有await 运算符的async 方法。这可以确保方法中的代码都被视为Task 的一部分。
  • 为避免 CS1998,您可以将 await Task.FromResult(0); 添加到您的方法中。这应该不会对性能产生任何重大影响(与 Task.Yield() 不同)。
  • @AndrewTheken:这些天你可以做return Task.CompletedTask; - 最简单的。
【解决方案2】:

另一种选择,如果您想保持函数体简单而不编写代码来支持它,只需使用#pragma 抑制警告:

#pragma warning disable 1998
public async Task<object> Test()
{
    throw new NotImplementedException();
}
#pragma warning restore 1998

如果这足够常见,您可以将禁用语句放在文件顶部并省略恢复。

http://msdn.microsoft.com/en-us/library/441722ys(v=vs.110).aspx

【讨论】:

    【解决方案3】:

    另一种保留 async 关键字的方法(如果您想保留它)是使用:

    public async Task StartAsync()
    {
        await Task.Yield();
    }
    

    填充该方法后,您可以简单地删除该语句。 我经常使用它,尤其是当一个方法可能等待某事但并非每个实现都实际执行时。

    【讨论】:

    • 这应该是公认的答案。有时接口实现不需要异步,这比将所有内容都包装在 Task.Run 调用中要干净得多。
    • 等待Task.CompletedTask; // 可能是更好的选择
    • @FrodeNilsen 由于某种原因Task.CompletedTask 似乎不再存在。
    • @SebastiánVansteenkiste .Net Framework 4.6->,UWP 1.0->,.Net Core 1.0->
    • @AndrewTheken 我花了一段时间才得出结论,这个答案和您的评论特别适用于实现为空或仅引发异常的情况(如原始问题中所示)。如果一个实现确实返回了一个值,那么Task.FromResult 似乎是更好的答案。就此而言,如果您打算抛出异常,似乎另一个答案已经开始发挥作用,Task.FromException 使这永远不是理想的解决方案。你同意吗?
    【解决方案4】:

    解决方案之间存在差异,严格来说,您应该知道调用者将如何调用异步方法,但默认使用模式假定方法结果为“.Wait()” - “return Task.CompletedTask" 是最好的解决方案。

        BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
    Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
    Frequency=3233537 Hz, Resolution=309.2589 ns, Timer=TSC
    .NET Core SDK=2.1.2
      [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
      Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2600.0
      Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
    
    
             Method |  Job | Runtime |         Mean |       Error |      StdDev |       Median |          Min |          Max | Rank |  Gen 0 |  Gen 1 |  Gen 2 | Allocated |
    --------------- |----- |-------- |-------------:|------------:|------------:|-------------:|-------------:|-------------:|-----:|-------:|-------:|-------:|----------:|
     CompletedAwait |  Clr |     Clr |    95.253 ns |   0.7491 ns |   0.6641 ns |    95.100 ns |    94.461 ns |    96.557 ns |    7 | 0.0075 |      - |      - |      24 B |
          Completed |  Clr |     Clr |    12.036 ns |   0.0659 ns |   0.0617 ns |    12.026 ns |    11.931 ns |    12.154 ns |    2 | 0.0076 |      - |      - |      24 B |
             Pragma |  Clr |     Clr |    87.868 ns |   0.3923 ns |   0.3670 ns |    87.789 ns |    87.336 ns |    88.683 ns |    6 | 0.0075 |      - |      - |      24 B |
         FromResult |  Clr |     Clr |   107.009 ns |   0.6671 ns |   0.6240 ns |   107.009 ns |   106.204 ns |   108.247 ns |    8 | 0.0584 |      - |      - |     184 B |
              Yield |  Clr |     Clr | 1,766.843 ns |  26.5216 ns |  24.8083 ns | 1,770.383 ns | 1,705.386 ns | 1,800.653 ns |    9 | 0.0877 | 0.0038 | 0.0019 |     320 B |
     CompletedAwait | Core |    Core |    37.201 ns |   0.1961 ns |   0.1739 ns |    37.227 ns |    36.970 ns |    37.559 ns |    4 | 0.0076 |      - |      - |      24 B |
          Completed | Core |    Core |     9.017 ns |   0.0690 ns |   0.0577 ns |     9.010 ns |     8.925 ns |     9.128 ns |    1 | 0.0076 |      - |      - |      24 B |
             Pragma | Core |    Core |    34.118 ns |   0.4576 ns |   0.4281 ns |    34.259 ns |    33.437 ns |    34.792 ns |    3 | 0.0076 |      - |      - |      24 B |
         FromResult | Core |    Core |    46.953 ns |   1.2728 ns |   1.1905 ns |    46.467 ns |    45.674 ns |    49.868 ns |    5 | 0.0533 |      - |      - |     168 B |
              Yield | Core |    Core | 2,480.980 ns | 199.4416 ns | 575.4347 ns | 2,291.978 ns | 1,810.644 ns | 4,085.196 ns |   10 | 0.0916 |      - |      - |     296 B |
    

    注意:FromResult 不能直接比较。

    测试代码:

       [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
       [ClrJob, CoreJob]
       [HtmlExporter, MarkdownExporter]
       [MemoryDiagnoser]
     public class BenchmarkAsyncNotAwaitInterface
     {
    string context = "text context";
    [Benchmark]
    public int CompletedAwait()
    {
        var t = new CompletedAwaitTest();
        var a = t.DoAsync(context);
        a.Wait();
        return t.Length;
    }
    
    [Benchmark]
    public int Completed()
    {
        var t = new CompletedTest();
        var a = t.DoAsync(context);
        a.Wait();
        return t.Length;
    }
    
    [Benchmark]
    public int Pragma()
    {
        var t = new PragmaTest();
        var a = t.DoAsync(context);
        a.Wait();
        return t.Length;
    }
    
    [Benchmark]
    public int Yield()
    {
        var t = new YieldTest();
        var a = t.DoAsync(context);
        a.Wait();
        return t.Length;
    }
    
        [Benchmark]
        public int FromResult()
        {
            var t = new FromResultTest();
            var t2 = t.DoAsync(context);
            return t2.Result;
        }
    
    public interface ITestInterface
    {
        int Length { get; }
        Task DoAsync(string context);
    }
    
    class CompletedAwaitTest : ITestInterface
    {
        public int Length { get; private set; }
        public async Task DoAsync(string context)
        {
            Length = context.Length;
            await Task.CompletedTask;
        }
    }
    
    class CompletedTest : ITestInterface
    {
        public int Length { get; private set; }
        public Task DoAsync(string context)
        {
            Length = context.Length;
            return Task.CompletedTask;
        }
    }
    
    class PragmaTest : ITestInterface
    {
        public int Length { get; private set; }
        #pragma warning disable 1998
        public async Task DoAsync(string context)
        {
            Length = context.Length;
            return;
        }
        #pragma warning restore 1998
    }
    
    class YieldTest : ITestInterface
    {
        public int Length { get; private set; }
        public async Task DoAsync(string context)
        {
            Length = context.Length;
            await Task.Yield();
        }
    }
    
        public interface ITestInterface2
        {
            Task<int> DoAsync(string context);
        }
    
        class FromResultTest : ITestInterface2
        {
            public async Task<int> DoAsync(string context)
            {
                var i = context.Length;
                return await Task.FromResult(i);
            }
        }
    

    }

    【讨论】:

    • 不幸的是,#pragma 似乎会产生开销。可能与您创建并完成了AsyncOperation 而不是返回CompletedTask 一样多的开销。能够告诉编译器当方法同步运行时可以跳过它是很好的。
    • 您认为Task.CompletedTaskTask.FromResult 的相似程度如何?知道会很有趣 - 如果您必须返回一个值,我希望 FromResult 将是最相似的,并且仍然是表现最好的。
    • 我会添加的。我认为在这种情况下状态机代码会更冗长,并且 CompletedTask 将获胜。让我们看看
    • 很高兴看到此更新为 .NET Core 2.2,因为 allocations in the async state-machines have been drastically improved
    • @Tseng 我已经在 .NET Core 2.2.0 上运行了基准测试。显然,总时间因硬件不同而有所不同,但比例大致相同: 方法 | .NET Core 2.0.3 平均值 | .NET Core 2.2.0 平均完成 | 100% | 100% 完成等待 | 412.57% | 377.22% 来自结果 | 520.72% | 590.89% 杂注 | 378.37% | 346.64% 收益率 | 27514.47% | 23602.38%
    【解决方案5】:

    我知道这是一个旧线程,也许这不会对所有用法都产生正确的效果,但以下内容尽可能接近在我尚未实现时能够简单地抛出 NotImplementedException一个方法,而不改变方法签名。如果它有问题,我很乐意知道它,但这对我来说几乎不重要:无论如何我只在开发时使用它,所以它的执行方式并不是那么重要。不过,如果是的话,我很乐意听到为什么这是一个坏主意。

    public async Task<object> test()
    {
        throw await new AwaitableNotImplementedException<object>();
    }
    

    这是我添加的类型以使其成为可能。

    public class AwaitableNotImplementedException<TResult> : NotImplementedException
    {
        public AwaitableNotImplementedException() { }
    
        public AwaitableNotImplementedException(string message) : base(message) { }
    
        // This method makes the constructor awaitable.
        public TaskAwaiter<AwaitableNotImplementedException<TResult>> GetAwaiter()
        {
            throw this;
        }
    }
    

    【讨论】:

      【解决方案6】:

      正如对 Stephen's Answer 的更新,您不再需要编写 TaskConstants 类,因为有一个新的辅助方法:

          public Task ThrowException()
          {
              try
              {
                  throw new NotImplementedException();
              }
              catch (Exception e)
              {
                  return Task.FromException(e);
              }
          }
      

      【讨论】:

      • 不要这样做。堆栈跟踪不会指向您的代码。必须抛出异常才能完全初始化。
      • Daniel B - 是的,你完全正确。我已修改我的答案以正确抛出异常。
      【解决方案7】:

      你可以试试这个:

      public async Task<object> test()
      {
      await Task.CompletedTask; 
      }
      

      【讨论】:

      • 我更喜欢,等待Task.Delay(0);
      【解决方案8】:

      如果您已经链接到 Reactive Extension,您也可以这样做:

      public async Task<object> NotImplemented()
      {
          await Observable.Throw(new NotImplementedException(), null as object).ToTask();
      }
      
      public async Task<object> SimpleResult()
      {
          await Observable.Return(myvalue).ToTask();
      }
      

      Reactive 和 async/await 本身就令人惊叹,但它们也可以很好地配合使用。

      需要的包括:

      using System.Reactive.Linq;
      using System.Reactive.Threading.Tasks;
      

      【讨论】:

        【解决方案9】:

        试试这个:

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS1998:Await.Warning")]

        见: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.suppressmessageattribute?view=netframework-4.7.2

        【讨论】:

          【解决方案10】:

          可能出现在cs1998下面。

          public async Task<object> Foo()
          {
              return object;
          }
          

          那么你可以在下面进行改造。

          public async Task<object> Foo()
          {
              var result = await Task.Run(() =>
              {
                  return object;
              });
              return result;
          }
          

          【讨论】:

            【解决方案11】:

            如果您没有任何等待,则返回 Task.FromResult

            public Task<int> Success() // note: no "async"
            {
              ... // Do not have await code
              var result = ...;
              return Task.FromResult(result);
            }
            

            【讨论】:

              【解决方案12】:

              全局配置/抑制它:

              在 .editorconfig 中

              # CS1998: Async method lacks 'await' operators and will run synchronously
              dotnet_diagnostic.CS1998.severity = suggestion
              

              在一个普通的、不是高性能的应用程序中,不必要的异步的开销可以忽略不计,而对于常规编码人员来说,大脑关闭异步的好处更为重要。 (+ 额外的编译器检查等)

              【讨论】:

              • 不必要的异步开销可以忽略不计”。这是真的,但它与你的其他答案有什么关系?抑制 CS1998 警告并不是要接受可以忽略不计的开销,而是要允许潜在的有害错误不被注意。
              • 呵呵,同步运行异步方法可能会引入哪些错误?
              • yannik 如果我的async 方法缺少await,我很可能忘记了await 应该等待的东西。
              • 不,这是一个不同的警告:# CS4014:由于不等待此调用,因此在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。
              • 假设var result = ProcessAsync(); 是一个错误,并且var result = await ProcessAsync(); 是正确的,CS1998 警告可能会帮助我检测错误。 CS4014 警告对我没有帮助,因为它不会被触发。这仅由如下代码触发:ProcessAsync();.
              【解决方案13】:

              这里有一些替代方法,具体取决于您的方法签名。

                  public async Task Test1()
                  {
                      await Task.CompletedTask;
                  }
              
                  public async Task<object> Test2()
                  {
                      return await Task.FromResult<object>(null);
                  }
              
                  public async Task<object> Test3()
                  {
                      return await Task.FromException<object>(new NotImplementedException());
                  }
              

              【讨论】:

                【解决方案14】:

                抑制此警告的最干净等待是在投掷前使用await Task.CompletedTask;。这是一个无操作的函数

                public async Task SomeMethod()
                {
                    await Task.CompletedTask;
                    throw new NotImplementedException();
                }
                

                【讨论】:

                  【解决方案15】:
                  // This is to get rid of warning CS1998, please remove when implementing this method.
                  await new Task(() => { }).ConfigureAwait(false);
                  throw new NotImplementedException();
                  

                  【讨论】:

                    【解决方案16】:

                    您可以从方法中删除 async 关键字并让它返回 Task;

                        public async Task DoTask()
                        {
                            State = TaskStates.InProgress;
                            await RunTimer();
                        }
                    
                        public Task RunTimer()
                        {
                            return new Task(new Action(() =>
                            {
                                using (var t = new time.Timer(RequiredTime.Milliseconds))
                                {
                                    t.Elapsed += ((x, y) => State = TaskStates.Completed);
                                    t.Start();
                                }
                            }));
                        }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2019-08-27
                      • 2019-11-21
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-02-13
                      • 2017-04-30
                      • 2017-10-15
                      • 1970-01-01
                      相关资源
                      最近更新 更多