CS1998 경고 표시 안 함 :이 비동기 메서드에는 'await'가 없습니다.


104

일부 비동기 기능이있는 인터페이스가 있습니다. 인터페이스를 구현하는 일부 클래스는 기다릴 것이 없으며 일부는 그냥 던질 수 있습니다. 모든 경고로 인해 약간 짜증이납니다.

비동기 함수에서 await를 사용하지 않을 때.

메시지를 숨길 수 있습니까?

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

경고 CS1998 :이 비동기 메서드에는 'await'연산자가 없으며 동 기적으로 실행됩니다. 'await'연산자를 사용하여 비 차단 API 호출을 기다리거나 'await Task.Run (...)'을 사용하여 백그라운드 스레드에서 CPU 바운드 작업을 수행하는 것을 고려하십시오.


1
async로 표시된 함수에서 새로운 await 키워드를 사용하지 않는 경우.
Simon

문제를 재현하는 코드 샘플을 보여 주면 어떨까요?
John Saunders

답변:


106

일부 비동기 기능이있는 인터페이스가 있습니다.

반환하는 방법 Task, 나는 믿는다. async구현 세부 사항이므로 인터페이스 메서드에 적용 할 수 없습니다.

인터페이스를 구현하는 일부 클래스는 기다릴 것이 없으며 일부는 그냥 던질 수 있습니다.

이러한 경우에는 다음과 같은 사실을 활용할 수 있습니다. async 구현 세부 사항 .

에 아무것도 없으면 await다음을 반환 할 수 있습니다 Task.FromResult.

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

throwing의 경우 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 개체를 입니다.

내 AsyncEx 라이브러리에 몇 가지 다른 "작업 상수"유형 예제가 있습니다 .


1
키워드를 잃어 버릴 생각은 없었습니다. 당신이 말했듯이 비동기는 인터페이스와 관련이 없습니다. 내 잘못이야, 고마워.
Simon

3
반환 유형이 작업 (결과 없음) 인 접근 방식을 추천 할 수 있습니까?
Mike

9
경고 : 이 접근 방식은 오류가 예상대로 전파되지 않기 때문에 문제를 일으킬 수 있습니다. 일반적으로 호출자는 메서드의 예외가 Task. 대신 메서드가 Task. 가장 좋은 패턴은 연산자 async없이 메서드 를 정의하는 것입니다 await. 이렇게하면 메서드 내의 코드가 모두 Task.
Bob Meyers

11
CS1998을 방지하려면 await Task.FromResult(0);메서드에 추가 할 수 있습니다 . 이것은 Task.Yield ()와 달리 성능에 큰 영향을 미치지 않아야합니다.
Bob Meyers

3
@AndrewTheken : 요즘 당신은 할 수 있습니다 return Task.CompletedTask;.
Stephen Cleary

63

또 다른 옵션은 함수 본문을 단순하게 유지하고이를 지원하는 코드를 작성하지 않으려는 경우 #pragma로 경고를 억제하는 것입니다.

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

이것이 충분히 일반적인 경우 파일 맨 위에 disable 문을 배치하고 복원을 생략 할 수 있습니다.

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


40

async 키워드를 보존하는 또 다른 방법 (보존하려는 경우)은 다음을 사용하는 것입니다.

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

메소드를 채우면 간단히 명령문을 제거 할 수 있습니다. 나는 특히 메소드가 무언가를 기다리고 있지만 모든 구현이 실제로는 그렇지 않을 때 이것을 많이 사용합니다.


이것은 받아 들여진 대답이어야합니다. 때로는 인터페이스 구현이 비동기 일 필요가 없습니다 Task.Run. 이것은 호출 에서 모든 것을 래핑하는 것보다 훨씬 더 깔끔 합니다.
Andrew Theken

12
await Task.CompletedTask; // 더 좋은 옵션이 될 수있다
프로드 닐슨

@FrodeNilsen은 어떤 이유로 Task.CompletedTask더 이상 존재하지 않는 것 같습니다.
Sebastián Vansteenkiste

1
@ SebastiánVansteenkiste .Net Framework 4.6->, UWP 1.0->, .Net Core 1.0->
Frode Nilsen

1
@AndrewTheken이 답변과 귀하의 의견이 구현이 비어 있거나 예외를 던지는 경우에만 적용된다는 결론에 도달하는 데 시간이 조금 걸렸습니다 (원래 질문에서와 같이). 구현이 값을 반환 Task.FromResult하면 더 나은 대답 인 것 같습니다 . 당신이 경우 그 문제를 들어, 하는 예외를 던질 것, 또 다른 대답에 대한 놀이로 온 것 Task.FromException이 결코 이상적인 솔루션을. 동의 하시겠습니까?
BlueMonkMN

15

솔루션과 엄밀히 말하면 호출자가 비동기 메서드를 호출하는 방법을 알아야하지만 메서드 결과에서 " .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);
        }
    }

}


1
#pragma하나에 오버 헤드가 발생 하는 것은 유감입니다 . 아마 그냥 많은 오버 헤드 대신에 반환하는 경우로 CompletedTask만든 및 완료 AsyncOperation. 어쨌든 메서드가 동 기적으로 실행될 때이를 건너 뛰어도된다고 컴파일러에게 말할 수 있으면 좋을 것입니다.
binki

어떻게 유사한 당신이 생각합니까는 Task.CompletedTask유사하다 Task.FromResult? 알아두면 흥미로울 것입니다. 값을 반환해야한다면 FromResult가 가장 유사하고 여전히 최고의 성능을 발휘할 것으로 기대합니다.
BlueMonkMN

추가하겠습니다. 나는 상태 머신 코드가이 경우에 더 자세한 될 것이라고 생각하고 CompletedTask는 이라니요 볼 이길 것이다
로마 Pokrovskij


1
@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 %
Storm

10

나는 이것이 오래된 스레드라는 것을 알고 있으며 아마도 이것은 모든 사용에 대해 올바른 효과를 갖지 못할 것입니다. 그러나 다음은 아직 메서드를 구현하지 않았을 때 단순히 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;
    }
}

10

Stephen의 답변에 대한 업데이트와 마찬가지로 TaskConstants새로운 도우미 메서드가 있으므로 더 이상 클래스 를 작성할 필요가 없습니다 .

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

3
이러지마 스택 추적은 코드를 가리 키지 않습니다. 완전히 초기화하려면 예외가 발생해야합니다.
Daniel B

1
Daniel B-네, 당신이 절대적으로 옳습니다. 예외를 올바르게 throw하도록 내 대답을 수정했습니다.
Matt

3

이미 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;

3

아래에서 cs1998이 발생할 수 있습니다.

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

그러면 아래에서 개혁 할 수 있습니다.

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

3

이것을 시도해 볼 수 있습니다.

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


1

기다릴 것이 없으면 Task.FromResult를 반환합니다.

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

1

다음은 메소드 서명에 따른 몇 가지 대안입니다.

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

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

-2

메서드에서 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();
            }
        }));
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.