When I stepped over breakpoints in my code I have encountered strange behaviour of debugger:
public async Task DoSomeWork()
{
await Task.Run(() => { Thread.Sleep(1000); });
var test = false;
if (test)
{
throw new Exception("Im in IF body!");
}
}
Debugger goes into if
body. It's remarkable that the exception is not really thrown but just looks like it is. So you can't reproduce that if you place breakpoint right on throw
. You must place it above and step down to the if
body to catch it. The same works on any kind of exception instance (as well as explicit null
) and even on return
instead of throw
.
Besides that it works even if I remove line with await
.
I tried to run this code snippet from different PCs so its not a PC trouble. Also I have thought it is bug in VS code and tried to run it in Rider from JetBrains - the same result.
I'm sure it's the async thing but how it explicitly works?
Your code reproduces the issue easily, in a "Debug" build, using Visual Studio 2015. I had only to add in Program.Main()
, with a call to DoSomeWork().Wait();
, set a breakpoint in the method and step through it.
As for why it happens, this is undoubtedly due to the combination of the async
method being rewritten and the debugging database (.pdb) generated. Similar to iterator methods, adding async
to the method causes the compiler to change your method into a state machine. The actual IL that's generated looks only a little bit like the original method. That is, if you look at it, you can identify the key components of the original code, but it's now in a big switch
statement that handles what happens as the method returns at each await
statement, and then is re-entered with the completion of each awaited expression.
When the program statement appears to be on the throw
, it's really at the implicit return
statement in the method. It's just that the debugging database for the executable doesn't provide a program statement for that line.
There's a hint, when debugging, that that's what's happening. When you step over the if
statement, you'll notice it goes straight to the throw
statement. If the if
statement block were really being entered, the next program statement line would actually be the opening brace for the block, not the program statement.
You can also add e.g. a Console.WriteLine()
at the end of the method, and that will give the debugger enough information to sync up and not show you at the wrong line number.
For additional information on how async
methods are handled by the compiler, see Is the new C# async feature implemented strictly in the compiler, and the links provided there (including Jon's series of articles on the topic).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With