[c#] CS1998 경고 표시 안 함 :이 비동기 메서드에는 ‘await’가 없습니다.

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

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

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

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

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



답변

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

반환하는 방법 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 라이브러리에 몇 가지 다른 “작업 상수”유형 예제가 있습니다 .


답변

또 다른 옵션은 함수 본문을 단순하게 유지하고이를 지원하는 코드를 작성하지 않으려는 경우 #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


답변

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

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

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


답변

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

}


답변

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


답변

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

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


답변

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