警告CS1998を抑制:この非同期メソッドには「待機」がありません


104

いくつかの非同期機能を備えたインターフェースを持っています。インターフェースを実装するクラスには、待機するものがないクラスもあれば、単にスローするクラスもあります。それはすべての警告で少し迷惑です。

非同期機能でawaitを使用しない場合。

メッセージを抑制することは可能ですか?

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

警告CS1998:この非同期メソッドには「待機」演算子がなく、同期的に実行されます。「await」演算子を使用して非ブロッキングAPI呼び出しを待機するか、「await Task.Run(...)」を使用してバックグラウンドスレッドでCPUにバインドされた作業を行うことを検討してください。


1
非同期としてマークされた関数で新しいawaitキーワードを使用しない場合。
Simon

問題を再現するコードサンプルを見せてくれませんか?
ジョンサンダース

回答:


106

いくつかの非同期機能を備えたインターフェースを持っています。

メソッドが戻るTaskと思います。async実装の詳細であるため、インターフェースメソッドには適用できません。

インターフェースを実装するクラスには、待機するものがないクラスもあれば、単にスローするクラスもあります。

これらの場合、async実装の詳細であるという事実を利用できます。

何もないawait場合は、単に返すことができますTask.FromResult

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

をスローする場合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;
}

同じ戻り値の型を持つ各メソッドがTaskNotImplementedExceptionオブジェクトを共有できるため、ヘルパークラスはGCが収集しなければならないガベージも削減します。

私のAsyncExライブラリには、他にもいくつかの「タスク定数」タイプの例があります


1
キーワードを失うことは考えていませんでした。あなたが言うように、非同期はインターフェースとは何の関係もありません。私の悪い、ありがとう。
Simon

3
戻り値の型が単なるタスク(結果なし)であるアプローチを推奨できますか
マイク

9
警告:この方法では、エラーが予期したとおりに伝播されないため、問題が発生する可能性があります。通常、呼び出し元は、メソッド内の例外が内で発生することを期待しますTask。代わりに、メソッドは、を作成する機会さえ得る前にスローしますTask。最良のパターンはasyncawait演算子のないメソッドを定義することです。これにより、メソッド内のコードはすべての一部として扱われTaskます。
Bob Meyers、2016年

11
CS1998を回避するためawait Task.FromResult(0);に、メソッドに追加できます。これは、(Task.Yield()とは異なり)パフォーマンスに大きな影響を与えません。
Bob Meyers、2016年

3
@AndrewTheken:最近できるのはreturn Task.CompletedTask;、最も簡単なことです。
Stephen Cleary 2016年

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 2016年

12
Task.CompletedTask;を待ちます。//より良いオプションかもしれません
Frode Nilsen 2017年

@FrodeNilsenは、何らかの理由でTask.CompletedTaskもう存在していないようです。
セバスティアンVansteenkiste

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

1
@AndrewThekenこの回答とあなたのコメントが、実装が空であるか、または例外をスローする(元の質問のように)場合のみに特に当てはまるという結論に達するには、しばらく時間がかかりました。実装が値を返す場合、それTask.FromResultはより良い答えのようです。あなたがあればそのことについては、されている例外をスローに行く、別の答えはに関する遊びに来ているようでTask.FromException、この決して理想的なソリューションとなっています。同意しますか
BlueMonkMN 2018年

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 2017

あなたはどのように類似したと思いますTask.CompletedTaskに似ていますかTask.FromResult?知っておくと興味深いでしょう。値を返す必要がある場合は、FromResultが最も類似していて、依然として最高のパフォーマンスを発揮すると思います。
BlueMonkMN 2018年

追加します。この場合、ステートマシンコードはより冗長になり、CompletedTaskが勝つと思います。
Roman Pokrovskij


1
@Tseng .NET Core 2.2.0でベンチマークを実行しました。明らかに、ハードウェアが異なるため合計時間は異なりますが、比率はほぼ同じままです。.NET Core 2.0.3の意味| .NET Core 2.2.0の平均完了| 100%| 100%完了しました 412.57%| 377.22%FromResult | 520.72%| 590.89%プラグマ| 378.37%| 346.64%の歩留まり| 27514.47%| 23602.38%

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's Answerの更新と同じように、TaskConstants新しいヘルパーメソッドがあるため、クラスを記述する必要がなくなりました。

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

3
これを行わないでください。スタックトレースはコードを指しません。完全に初期化するには、例外をスローする必要があります。
Daniel B

1
ダニエルB-はい、あなたは完全に正しいです。例外を正しくスローするように答えを変更しました。
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;
}



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.