简体   繁体   中英

Suppress warning CS1998: This async method lacks 'await'

I've got an interface with some functions that return Task . Some of the classes that implement the interface do not have anything to await, while others might just throw - so the warnings are spurious and annoying.

Is it possible to suppress these warnings? Eg:

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

yields:

warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

I've got an interface with some async functions.

Methods returning Task , I believe. async is an implementation detail, so it can't be applied to interface methods.

Some of the classes that implements the interface does not have anything to await, and some might just throw.

In these cases, you can take advantage of the fact that async is an implementation detail.

If you have nothing to await , then you can just return Task.FromResult :

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

In the case of throwing NotImplementedException , the procedure is a bit more wordy:

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

If you have a lot of methods throwing NotImplementedException (which itself may indicate that some design-level refactoring would be good), then you could wrap up the wordiness into a helper class:

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

The helper class also reduces garbage that the GC would otherwise have to collect, since each method with the same return type can share its Task and NotImplementedException objects.

I have several other "task constant" type examples in my AsyncEx library .

Another option, if you want to keep the body of the function simple and not write code to support it, is simply to suppress the warning with #pragma:

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

If this is common enough, you could put the disable statement at the top of the file and omit the restore.

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

Another way to preserve the async keyword (in case you want to keep it) is to use:

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

Once you populate the method you can simply remove the statement. I use this a lot especially when a method might await something but not every implementation actually does.

There is difference between solutions and strictly speaking you should know how caller is going to call the async method, but with default usage pattern that assumes ".Wait()" on method result - " return Task.CompletedTask " is the best solution.

    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 |

Note: FromResult can't be directly compared.

Test Code:

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

}

I know this is an old thread, and perhaps this won't have the right effect for all usages, but the following is as close as I can get to being able to simply throw a NotImplementedException when I haven't yet implemented a method, without altering the method signature. If it's problematic I'd be happy to know about it, but it barely matters to me: I only use this while in development anyway, so how it performs isn't all that important. Still, I'd be happy to hear about why it's a bad idea, if it is.

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

Here's the type I added to make that possible.

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

Just as an update to Stephen's Answer, you no longer need to write the TaskConstants class as there is a new helper method:

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

You might try this:

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

In case you already link against Reactive Extension, you can also do:

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 and async/await are both amazing in and by themselves, but they also play well together.

Includes needed are:

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

It might be occured cs1998 below.

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

Then you can reform below.

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

Try this:

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

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

If you don't have anything to await then return Task.FromResult

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

Here is some alternatives depending on your method signature.

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

First, I found a trick to bypass this warning:

public async Task<object> test()
{
    //a pseudo code just to disable the warning about lack of await in async code!
    var xyz = true ? 0 : await Task.FromResult(0); //use a var name that's not used later

    //... your code statements as normal, eg:
    //throw new NotImplementedException();
}

This is a pseudo code to disable warning about lack of await in an asycn method! the existence of that await keyword there will fraud the compiler to don't raise the warning, even if we know it will never be called! as the condition is true it always return the first part of the ternary conditional (?:), and also as this var is a non-using var, it will omitted in Release builds. I'm not sure if there is any side effects with this approach.


Also, I found this simple method is used in source code of asp.net core (TagHelper.cs), butit need to remove the async keyword from the method signature line:

return Task.CompletedTask;

Here is the more complete code:

public abstract class TagHelper : ITagHelper, ITagHelperComponent
{

    public virtual void Process(TagHelperContext context, TagHelperOutput output)
    {
    }

    public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        Process(context, output);
        return Task.CompletedTask; // *** this will avoid the lacks await warning!
    }
}

Configure/Suppress it globally:

in .editorconfig

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

In a common, not high performance, application the overhead of unneccessary async is negligible, and the benefits of brain-off async-all-the-way for regular coders is more important. (+ additional compiler checks etc.)

// This is to get rid of warning CS1998, please remove when implementing this method.
await new Task(() => { }).ConfigureAwait(false);
throw new NotImplementedException();

You can drop the async keyword from the method and just have it return 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();
            }
        }));
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM