Comportamiento extraño del depurador en un método asíncrono

Cuando superé los puntos de interrupción en mi código, encontré un comportamiento extraño del depurador:

public async Task DoSomeWork() { await Task.Run(() => { Thread.Sleep(1000); }); var test = false; if (test) { throw new Exception("Im in IF body!"); } } 

Depurador entra en if cuerpo. Es notable que la excepción no sea realmente lanzada, pero parece que sí. Así que no puedes reproducir eso si colocas un punto de corte justo en el throw . Debes colocarlo en la parte superior y bajar al cuerpo para atraparlo. Lo mismo funciona en cualquier tipo de instancia de excepción (así como en null explícito) e incluso en return lugar de throw .

Además de eso funciona incluso si elimino la línea con await .

Intenté ejecutar este fragmento de código desde diferentes PC, por lo que no es un problema de PC. También he pensado que es un error en el código VS y traté de ejecutarlo en Rider desde JetBrains – el mismo resultado.

Estoy seguro de que es asíncrono, pero ¿cómo funciona explícitamente?

Su código reproduce el problema fácilmente, en una comstackción “Debug” , utilizando Visual Studio 2015. Solo tuve que agregar Program.Main() , con una llamada a DoSomeWork().Wait(); , establecer un punto de interrupción en el método y pasar a través de él.

En cuanto a por qué sucede, esto se debe sin duda a que la combinación del método async se está reescribiendo y la base de datos de depuración (.pdb) generada. De manera similar a los métodos de iterador, agregar async al método hace que el comstackdor cambie su método a una máquina de estado. La IL real que se genera se parece un poco al método original. Es decir, si lo mira, puede identificar los componentes clave del código original, pero ahora está en una gran instrucción de switch que maneja lo que sucede a medida que el método retorna en cada instrucción de await , y luego se vuelve a ingresar al finalizar De cada expresión esperada.

Cuando la statement del progtwig parece estar en el throw , es realmente en la statement de return implícita en el método. Es solo que la base de datos de depuración para el ejecutable no proporciona una statement del progtwig para esa línea.

Hay una pista, al depurar, que eso es lo que está pasando. Cuando pase por encima de la instrucción if , notará que va directamente a la instrucción throw . Si realmente se ingresara el bloque de instrucciones if , la siguiente línea de la statement del progtwig sería en realidad la llave de apertura del bloque, no la statement del progtwig.

También puede agregar, por ejemplo, una Console.WriteLine() al final del método, y eso le dará al depurador suficiente información para sincronizarse y no mostrarle el número de línea incorrecto.

Para obtener información adicional sobre cómo el comstackdor maneja los métodos async , consulte ¿Se implementó estrictamente la nueva característica de C # async en el comstackdor , y los enlaces que se proporcionan allí (incluida la serie de artículos de Jon sobre el tema)?