ChatGPT解决这个技术问题 Extra ChatGPT

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? E.g.:

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.

When not using the new await keyword in a function marked as async.
How about showing us a code sample that reproduces the problem?

L
Lorenzo Polidori

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.


I did not think of losing the keyword. As you say, async got nothing to do with the interface. My bad, thank you.
Can you recommend an approach where the return type is just Task (without a result?)
Warning: This approach can cause problems because errors will not be propagated the way you expect. Normally the caller will expect an exception in your method to be surfaced within the Task. Instead, your method will throw before it even gets a chance to create a Task. I really think the best pattern is to define an async method with no await operators. This ensures the code within the method all gets treated as part of the Task.
To avoid CS1998, you can add await Task.FromResult(0); to your method. This should not have any significant perf impact (unlike Task.Yield()).
@AndrewTheken: These days you can just do return Task.CompletedTask; - the simplest of all.
J
Jamie Macia

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


Or in project settings, Build, Errors and Warnings, Suppress specific warnings, add 1998. The way I see it, it's easier to write async everywhere rather than explicitly deal with the Task type everywhere, when only select implementations of an interface need await.
S
Simon Mattes

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.


This should be the accepted answer. Sometimes the interface implementations don't need to be async, this is much cleaner than wrapping everything in a Task.Run call.
await Task.CompletedTask; // might be a better option
@FrodeNilsen for some reason Task.CompletedTask does not seem to exist anymore.
@SebastiánVansteenkiste .Net Framework 4.6->, UWP 1.0->, .Net Core 1.0->
@AndrewTheken It took me a while to reach the conclusion that this answer and your comment apply specifically to the case where the implementation is empty or just throws an exception (as in the original question). If an implementation does return a value, it seems like Task.FromResult is the better answer. For that matter, if you are going to throw an exception, it seems another answer has come into play regarding Task.FromException making this never the ideal solution. Would you agree?
R
Roman Pokrovskij

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

}


It is unfortunate that the #pragma one seems to incur overhead. Probably just as much overhead as if instead of returning CompletedTask you created and completed an AsyncOperation. It’d be nice to be able to tell the compiler that it’s OK to skip that when the method runs synchronously anyway.
How similar do you suppose Task.CompletedTask is similar to Task.FromResult? It would be interesting to know - I expect FromResult would be most analogous and still the best performer if you have to return a value.
I will add it. I think state machine code will be more verbose in this case and CompletedTask will win .Let see
Would be nice seeing this updated for .NET Core 2.2, since allocations in the async state-machines have been drastically improved
@Tseng I've run the benchmarks on .NET Core 2.2.0. Obviously, the total time is different due to different hardware, but the ratio remains roughly the same: Method | .NET Core 2.0.3 Mean | .NET Core 2.2.0 Mean Completed | 100% | 100% CompletedAwait | 412.57% | 377.22% FromResult | 520.72% | 590.89% Pragma | 378.37% | 346.64% Yield | 27514.47% | 23602.38%
r
rrreee

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

M
Matt

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

Don’t do this. The stack trace will not point at your code. Exceptions must be thrown to be completely initialized.
Daniel B - Yes, you are absolutely right. I have modified my answer to correctly throw the exception.
F
Faisal Mehboob

You might try this:

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

I prefer, await Task.Delay(0);
@alv why do you prefer Task.Delay if I may ask ?
@TheLegendaryCopyCoder I only suppress these (annoying) warnings on really beta code so when I approach a release I would do a search for Task.Delay(0) in the codebase and I can find these occurrences. If I searched for Task.CompletedTask it could highlight false positives... just personal preference but another way of doing it :)
@alv I see. I'm going to do the same, tnx.
L
Lorenzo Polidori

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;

P
Paul Schroeder

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


y
yannik

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.)


"the overhead of unnecessary async is negligible". This is true, but how is it related with the rest of your answer? Suppressing the CS1998 warning is not about accepting a negligible overhead, it is about allowing potentially harmful bugs to pass unnoticed.
huh, which bugs could possibly being introcuded by running an async method synchronously?
yannik if my async method lacks await, it is quite possible that I forgot to await something that should be awaited.
no, that's a different warning: # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Assuming that var result = ProcessAsync(); is a bug, and var result = await ProcessAsync(); is correct, the CS1998 warning may help me detect the bug. The CS4014 warning will not help me because it won't be triggered. This is only triggered with code like this: ProcessAsync();.
瀧谷賢司

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

p
priyanka

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

L
Ludde

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

D
DLeh

Cleanest wait to suppress this warning is to just use await Task.CompletedTask; before throwing. This functions as a no-op

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

A
AnGG

Use throw of yielded exception

public async Task<object> DoSomethingAsync()
{
    throw await Task.FromException<NotImplementedException>(new NotImplementedException());
}

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

R
Rakz

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