69

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

使用しない場合は非同期関数で待ちます。

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

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

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


  • 「非同期関数でawaitを使用しない場合」とはどういう意味ですか? - John Saunders
  • asyncとマークされた関数で新しいawaitキーワードを使用しない場合 - Simon
  • 問題を再現するコードサンプルを見せてください。 - John Saunders
  • 例を追加しました。 - Simon

13 답변


77

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

返すメソッド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;
}

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

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


  • 私はキーワードを失うことを考えませんでした。あなたが言うように、asyncはインターフェースとは何の関係もありません。私の悪い、ありがとう。 - Simon
  • 戻り値の型が単にTaskである(結果がない)アプローチを推奨できますか? - Mike
  • @マイク:以来Task<T>から継承Task、同じ解決策(Task.FromResultTaskCompletionSource)うまく働きます。単なるプレーンを作成するための簡単で効率的な方法はありません。Taskそのようなので、それを作成するのが最善ですTask<T>そして(暗黙のうちに)それをにキャストするTask。 - Stephen Cleary
  • 警告:この方法では問題が発生する可能性があります。エラーは想定どおりには伝播されないためです。通常、呼び出し元はメソッド内で例外が発生することを期待します。Task。その代わりに、あなたのメソッドはそれを作成する機会さえも得る前に投げます。Task。最良のパターンは、asyncなしの方法await演算子。これにより、メソッド内のコードはすべての一部として処理されます。Task。 - Bob Meyers
  • CS1998を回避するために、追加することができますawait Task.FromResult(0);あなたの方法に。これは(Task.Yield()とは異なり)パフォーマンスに大きな影響を与えません。 - Bob Meyers

51

関数の本体を単純にし、それをサポートするコードを記述したくない場合は、#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


32

asyncキーワードを保持するもう1つの方法は(それを保持したい場合)、次のように使用することです。

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

メソッドを作成したら、文を削除するだけです。 メソッドが何かを待つかもしれないが、すべての実装が実際にはそうではないとき、私は特にこれをたくさん使います。


  • これは受け入れられた答えであるはずです。インターフェイスの実装を非同期にする必要がない場合もあります。すべてをインターフェイスにラップするよりもはるかにクリーンです。Task.Runコール。 - Andrew Theken
  • Task.CompletedTaskを待ちます。 //より良い選択肢かもしれません - Frode Nilsen
  • 何らかの理由で@FrodeNilsenTask.CompletedTaskもう存在しないようです。 - Sebastián Vansteenkiste
  • @ Sebastiá nVansteenkiste .Net Framework 4.6->、UWP 1.0->、.Net Core 1.0-> - Frode Nilsen
  • @AndrewThekenこの答えとあなたのコメントは、実装が空の場合、または単に元の質問のように単に例外をスローする場合に特に当てはまるという結論に達するまで少し時間がかかりました。実装が値を返す場合、それはように見えますTask.FromResultより良い答えです。それならば、あります例外をスローしようとしている、それは別の答えが出てきたようですTask.FromExceptionこれを決して理想的な解決策にしないでください。あなたは同意しますか? - BlueMonkMN

7

私はこれが古いスレッドであることを知っています、そしておそらくこれはすべての用途に正しい効果をもたらすわけではありませんが、次のコードはまだメソッドを実装していないときに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;
    }
}


7

Stephen's Answerの更新と同じように、もう書く必要はありません。TaskConstants新しいヘルパーメソッドがあるのでクラス:

return Task.FromException(new NotImplementedException());


6

あなたが答えで見つけることができるさまざまな解決策の違いがあり、厳密に言えば、呼び出し側がどのようにasyncメソッドを呼び出そうとしているか知っておくべきですが、メソッドの結果に ".Wait()"を仮定する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);
        }
    }

}


  • 残念ながら、#pragmaオーバーヘッドが発生するようです。おそらく返すのではなく、ちょうど同じくらいのオーバーヘッドCompletedTaskを作成して完了しましたAsyncOperation。とにかくメソッドが同期的に実行されるときにそれをスキップしても大丈夫であることをコンパイラに伝えることができればうれしいです。 - binki
  • どれくらい似ていると思いますかTask.CompletedTaskと類似していますTask.FromResult?知っておくと面白いかもしれません - 値を返す必要がある場合は、FromResultが最も類似していて、それでも最高のパフォーマンスを発揮すると私は思います。 - BlueMonkMN
  • 追加します。この場合は、ステートマシンコードがより冗長になり、CompletedTaskが勝つと思います。 - Roman Pokrovskij

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;


2

下記の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();


0

これを試して:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS1998:Await.Warning")]

見る:https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.suppressmessageattribute?view=netframework-4.7.2


-1

メソッドから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();
            }
        }));
    }

リンクされた質問


関連する質問

最近の質問