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가 수집해야하는 가비지를 줄입니다. 동일한 반환 유형을 가진 각 메서드는 해당 Task및NotImplementedException 개체를 입니다.

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