2017-03-01 31 views
2

私は自分のコードにブレークポイントをステップオーバーすると、私は、デバッガの奇妙な行動に遭遇した:奇妙なデバッガの動作

public async Task DoSomeWork() 
{ 
    await Task.Run(() => { Thread.Sleep(1000); }); 

    var test = false; 
    if (test) 
    { 
      throw new Exception("Im in IF body!"); 
    } 
} 

デバッガはif体内に入ります。例外が本当にスローされていないが、まるでそうであるように見えることは注目に値する。したがって、ブレークポイントをthrowに置くと、それを再現することはできません。あなたはそれを上に置き、それを捕まえるためにif体に降ろす必要があります。どのような種類の例外インスタンスでも(明示的にnull)、throwの代わりにreturnにさえも同じことが成り立ちます。

さらに、awaitで行を削除しても機能します。

私はこのコードスニペットを別のPCから実行しようとしましたので、PCの問題ではありません。また、私はそれがVSコードのバグだと思って、JetBrainsからRiderで実行しようとしました - 同じ結果。

私はそれが非同期のものだと確信していますが、明示的にどのように動作しますか?

答えて

5

あなたのコードは、私が唯一、DoSomeWork().Wait();を呼び出して、Program.Main()に追加する方法にブレークポイントを設定し、それをステップ実行しなければならなかったのVisual Studio 2015を使用して、「デバッグ」ビルドで、簡単に問題を再現します。

なぜ発生するのかは、間違いなくasyncメソッドの書き換えとデバッグデータベース(.pdb)の組み合わせによるものです。反復子メソッドと同様に、メソッドにasyncを追加すると、コンパイラはメソッドを状態マシンに変更します。生成された実際のILは、元のメソッドのように少ししか見えません。つまり、元のコードの主要なコンポーネントを特定できますが、awaitステートメントでメソッドが返されたときに何が起きたのかを処理している大きなswitchステートメントに入っています。それぞれの待っている表現の

プログラムステートメントがthrowにあるように見える場合、それは実際にはメソッドの暗黙のreturnステートメントにあります。実行可能ファイル用のデバッグデータベースがその行のプログラム文を提供していないことだけです。

デバッグ時に、何が起こっているのかというヒントがあります。 ifステートメントをステップオーバーすると、throwステートメントにまっすぐに進みます。 ifステートメントブロックがの場合、実際にと入力された場合、次のプログラムステートメント行は実際にはプログラムステートメントではなくブロックの開始ブレースになります。

メソッドの最後にConsole.WriteLine()があり、それはデバッガに同期のための十分な情報を与え、間違った行番号であなたを表示しません。

asyncメソッドがコンパイラによってどのように処理されるかの詳細については、Is the new C# async feature implemented strictly in the compilerおよびそのリンク(トピックのJonシリーズを含む)を参照してください。

+0

あなたは 'async'と少なくとも1つの 'await'を追加したと言っていましたが、' await'の行がなくても再現しました。 – Altav1sta

+0

@ Altav1sta:そうです、コンパイラは、非同期メソッドです。私はあなたの記事の中で "うまくいく"と解釈して、デバッガが間違ったことではなく_right_のことをしたことを意味しています。私はそれがあなたが意味するものではないと思う。 –

+0

はい、混乱して申し訳ありません。私は__itが同じ働きをしていることを意味していました。つまり、 'if'ボディに入りましたが例外は本当に投げられません – Altav1sta