Depurando con .NET

by Valeriano Tortola 4. septiembre 2007 17:57

No siempre llenar el código de breakpoints ó seguir step-by-step ejecuciones largas es lo más recomendable para depurar el funcionamiento de nuestra aplicación en desarrollo , podemos apoyarnos en las clases Debugger, Debug y Trace de System.Diagnostics para realizar diagnósticos más rápidos y sobre todo que sean condicionados, de forma que solo pare ó solo registre una línea de log de actividad cuando nos interesa que lo haga.

Así que pasando un poco de las inestimables ayudas de Visual Studio 2005 para estos menesteres esta vez, voy a dar un resumen rápido de la utilidad de estas clases de ayuda al depurado. Las clases Debug y Trace son prácticamente iguales, la diferencia estriba en que la primera solo funciona en modo DEBUG y la segunda también en RELEASE. Por lo tanto lo que escriba de uno es aplicable al otro, pero tened en cuenta esta diferencia. Por ejemplo, el DefaultTraceListener (que se explica más adelante) está entre los listeners de Trace, no de Debug, aunque sea el que va conectado a la ventana ouput de Visual Studio 2005 ;)

Antes de entrar en materia, hay que echar un vistazo a los denominados listeners. Son, como indica su nombre, objetos a la escucha de la salida del depurador ó de las clases Debug ó Trace. Pueden ser de varios tipos: DefaultTraceListener, conectado a la ventana Output del IDE; TextWriterTraceListener, capaz de escribir a un archivo de texto plano ó aún Stream ó derivado; ConsoleTraceListener, escribe al a salida estandar; EventLogTraceListener, capaz de escribir al EventLog del sistema; XmlWriterTraceListener, capaz de escribir en forma de XML a un archivo de texto plano ó a ún Stream ó derivado y DelimitedListTraceListener, similar a TextWriterTraceListener pero con la particularidad de poder definir un caracter ó caracteres delimitadores.

Para añadir listeners a Debug ó a Trace, basta con instanciarlas y añadirlas a la lista:

 

Trace.Listeners.Add(new TextWriterTraceListener("Output.log"));

 

Ahora podremos, por ejemplo, redirigir toda la salida de Debug.Print() a un archivo de texto. Es interesante destacar también que las clases Debug y Trace permiten establecer niveles de identación a la hora de escribir sus registros de actividad, pudiendo aumentarla con el método .Ident() y reducirla con .Unident(), siendo .IdentSize quien determina la cantidad de espacios que se dan cada vez. También podemos manipular la identación a través de la propiedad .IdentLevel. Tened en cuenta que es imprescindible marcar la propiedad .AutoFlush = True para que la identación se produzca correctamente, pues afecta a lo que se escriba a partir de ese momento.

Para escirbir en los listeners, podemos hacer uso de los métodos: .Print(), que escribe en todos los listeners de Trace (únicamente, aunque se le llame desde Debug) ; .Write(), que escribe a todos los listeners de Debug y Trace; .WriteIf(), como .Write() pero solo si se cumple una condición dada; .WriteLine(), como .Write() pero añadiendo el delimitador de fin de línea ; .WriteLineIf(), como .WriteLine() pero solo si se cumple una condición dada.

.Fail() escribe un mensaje de error con el encabezado “Fail:” en los listeners de Trace y sobre el DefaultTraceListener hace saltar una ventana que nos da la opción de anular la ejecución, depurar paso a paso ó proseguir. Por último, el método .Assert() será el más útil de todos, ya que permite hacer un .Fail() evaluando una determinada condición, que viene mucho mejor que poner un breakpoint y estar pasando ciclos hasta llegar al caso que nos interesa. Por ejemplo, si tenemos un bucle y quisiéramos pararlo cuando el valor del iterador sea el carácter ‘a’, en vez de poner un punto de ruptura cada vez que se evalúe el iterador ó escribir una condición ‘if’ con un breakpoint … es más sencillo realizar un Assert para parar llegado el caso:

 

foreach (char c in "http://www.vtortola.net")
  Debug.Assert(c != 'a', "Se encontró una 'a' !!"); 

 

Siempre que no se cumpla la condición dada, se interrumpirá la ejecución y nos saldrá un dialogo que nos dará tres opciones: Abort(Anular) para parar la ejecución, Retry(Reintentar) para parar la ejecución en la línea del Assert y poder seguir depurando paso a paso y por último Ignore(Omitir) para seguir con la ejecución.

assert_fail

Como útimo tip, un minuto de atención a la propiedad .AutoFlush y el método .Flush(), que seguro que a todo el mundo suenan de trabajar con Streams :). Es recomendable marcar .AutoFlush como True para que se vacie el buffer cada vez que se escribe ó en su defecto asegurarnos (try-finally) que siempre se llama al método .Flush() antes de cerrar la aplicación, de lo contrario podríamos perder datos.

La clase Debugger nos permite comunicarnos con un depurador, la propiedad .IsAttached nos permite saber si hay conectado un depurador en un determinado momento de la ejecución , .IsLoggin() nos indica si hay algún “listener” registrando la actividad en ese momento, el método .Log() nos permite enviar mensajes al listener del depurador conectado en ese momento y el método .Break() permite parar la ejecución en ese punto como si hubiesemos puesto un breakpoint previamente. Este último es el más destacable, pues podríamos detener la ejecución si se diese una determinada condición, aunque sigue siendo preferible el método Debug.Assert () :)

Tags:

.NET 2.0 | C# 2.0

Comentarios

04/09/2007 22:56:26 #

trackback

Trackback from vtortola

Depurando con .NET

vtortola |

01/11/2007 0:51:47 #

PabloAbraham

Muy buen post es la primera vez que visito tu sitio, es muy interesante, lo seguire consultando

PabloAbraham México |

15/01/2009 10:15:41 #

pingback

Pingback from fiquepordentro.net

Fique por dentro Delimitadores  » Blog Archive   » Depurando con .NET

fiquepordentro.net |

Comentarios no permitidos