2015-09-24 10 views
7

Visual Studioはこのコードに対して警告を出します( 'この呼び出しは待機されていないため、呼び出しが完了する前に現在のメソッドの実行が継続されます)。非同期メソッドをvoidを返すと宣言して、CS4014の警告を消すことはできますか?

static void Main(string[] args) 
{ 
    FireAndForget(); // <-- Warning CS4014 
    // Do something else. 
} 

static async Task FireAndForget() 
{ 
    // Do something (cannot throw). 
} 

私の理解では、ファイア・アンド・フォーゲットが例外をスローすることはありませんので、この特定のケースでの作業を待つことはないOKであるということです。

プラグマで警告を無効にする代わりに、FireAndForgetの戻り値の型をTaskからvoidに変更することを検討していました。コンパイラを効果的に消すことができます。

static async void FireAndForget() // <-- Task changed to void 
{ 
    // Do something (cannot throw). 
} 

しかし、Stephen Clearyによると、「非同期のボイド」メソッドは、私は何をすべきかは非常にわからない避けるべきです。

メソッドが最初に待たされるように設計されておらず、例外がスローされない場合は、 '非同期void'メソッドを使用しても問題ありませんか?

+0

*最初に待たされる方法はありません。*どういう意味ですか? 'FireAndForget'は実際に何をしていますか? –

+3

なぜこれは落ちた+閉鎖の投票? – ken2k

+0

@ ken2k私は閉会に投票しませんでしたが、客観的に正解のない意見ベースの質問として、これがどこに見えるのか分かります。たぶん「それはOKですか?」と言い換えることができます。「利点と欠点は何ですか?」 – pseudocoder

答えて

9

それは極端に本当の火災と忘れ操作をすることは稀です。つまり、操作は次のとおりです。

  • 完了時に誰も気にしません。
  • 完了したら誰も気にしません。
  • 例外をスローすると誰も気にしません。

特にこれらの最後のもの。ほとんどのいわゆる「火災と忘れ」の操作は、成功しないと何らかの処置を取らなければならないため、実際には火災や忘れがありません。

しかし、実際の火災と忘れが該当する状況がいくつかあります。

Iは、async Taskを使用し、そうでなければ未使用の変数にタスクを割り当てることによって、コンパイラの警告を回避することを好む:

var _ = FireAndForget(); 

async Task方法はasync void方法よりも再利用可能とテスト可能です。

私のチームのデベロッパーがちょうどasync voidを使用した場合、私はフィットを捨てません。

+0

私はそれが稀でなければならないと信じることは難しいと思う。 fire-and-forgetメソッドがなければ、「async」はウィルスにならず、Mainメソッド(Wait)に伝播しませんか? – ZunTzu

+0

スティーブン、別の質問があります。あなたの例のような変数にタスクを割り当てようとしました。実際には、コンパイラは消えていますが、タスクはまだ待機していません。コンパイラもそのような場合に警告を出すべきではありませんか? – ZunTzu

+1

@ ZunTzu:1)はい。それはほとんどの(> 98%)非同期プログラムが動作する方法です。 2)コンパイラは警告するのに最善を尽くしますが、完璧ではありません。 –

2

それは、メソッドが最初の場所にしても例外はスローされません場合awaitableする を設計されていない場合は、「非同期のボイド」メソッドを持ってOKですか?

これは「OK」でも構いませんが、私はまだ方法async Taskを作成することをお勧めします。このメソッドが100%確実にスローされず、待たれるべきではありませんが、そのメソッドがどのように使用されるのかはわかりません。あなたは今async voidの使用の結果が何であるか完全に認識していますが、誰かがこれを将来使用する必要があるかもしれない若干の機会があれば、なぜこのTaskが存在しないのかこれを作る簡単な道に行くのではなく、待っていたvoid

コンパイラの警告が心配しないようにしてください。コードベースに与える正当性と影響について心配しています。

+0

メソッドが 'async'であるために 'void'を 'Task'に変更するのは間違っています。これが理由です。 '非同期'の唯一の目的は、メソッドの本体で 'await'を使用する可能性をロック解除することです。私の意見は、それは '非同期'がメソッドの外部契約の一部ではないということです。コントラクト上の 'void'から 'Task'に変更すると、その外部契約が変更されます。 – ZunTzu

+0

@ ZunTzuしかし、それはメソッドの真の操作を公開します。私が 'void'を返すメソッドを見たとき、私はそれが同期していると安全に仮定します。完了するそのようなメソッドを呼び出すが、その基本的な操作は、方法がより混乱していない可能性があり、呼び出し側に潜んでいる可能性があります。 –

+0

一般的なルールとして、呼び出し側はワーカースレッドを起動するか、Webサービスを呼び出すかを判断できません。なぜ私は '待って'を使用するかどうかを呼び出し側が知る必要がありますか?カプセル化の原則に違反しているように思えます。 – ZunTzu

0

潜在的な問題点は、コードが例外をスローしたかどうかを判断することが不可能になることです。したがって、これらを検出するためのユニットテストがある場合、ユニットテストは決してうまくいかないでしょう。

MSDNサイトからクラシック例:

 
private async void ThrowExceptionAsync() 
{ 
    throw new InvalidOperationException(); 
} 
public void AsyncVoidExceptions_CannotBeCaughtByCatch() 
{ 
    try 
    { 
    ThrowExceptionAsync(); 
    } 
    catch (Exception) 
    { 
    // The exception is never caught here! 
    throw; 
    } 
} 

http://haacked.com/archive/2014/11/11/async-void-methods/

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

代わりのasync voidを使用して、これは特定のエッジの場合であれば、どのようにプラグマ・ステートメントを使用してはどうですか?

#pragma warning disable CS-4014 
... your code here ... 
#pragma warning restore CS-4014 

このようにして静的ノイズを調整できます。

HTH ...

+0

私はその制限を認識しています。だから私は明示的に書いたFireAndForgetは例外をスローすることはできません。 – ZunTzu

+0

ええ、 'async void 'の特定の意図された使い方は実際にはイベントハンドラのためです。だから、外からの使用は避けなければなりません、IMHO。代わりにプラグマを使用してください。 – code4life