Depuración de código C#: de «¿por qué no funciona?» a «sé dónde está el error»
\n\nCada desarrollador de C# tarde o temprano se enfrenta a una situación en la que el código se compila, se ejecuta, pero hace algo completamente diferente a lo esperado. O falla con un error que es imposible de reproducir en el entorno de pruebas. En esos momentos, la depuración de C# pasa de ser una tarea rutinaria a un verdadero arte.
\n\nEn este artículo, analizaremos técnicas y herramientas que le ayudarán no solo a encontrar el bug, sino a hacerlo de forma rápida y eficiente. No nos limitaremos al estándar F5: hablaremos sobre puntos de interrupción avanzados, expresiones condicionales, análisis de memoria e incluso trabajo con volcados en WinDbg. ¡Allá vamos!
\n\nTécnicas de depuración de C# básicas pero potentes
\n\nAntes de pasar a herramientas exóticas, asegurémonos de que está aprovechando al máximo Visual Studio. Muchos desarrolladores solo usan F9 (poner un punto de interrupción) y F10 (paso por encima), y es una lástima.
Puntos de interrupción condicionales (Conditional Breakpoints)
\n\nImagine: tiene un bucle de 1000 iteraciones y el bug solo aparece en la 567. ¿Poner un punto de interrupción y presionar F5 567 veces? No. Use puntos de interrupción condicionales.
\n\nfor (int i = 0; i < 1000; i++)\n{\n // Alguna lógica\n ProcessItem(items[i]);\n}\n\nHaga clic derecho en el círculo rojo del punto de interrupción → seleccione «Conditions» → ingrese i == 567. Cuando el contador llegue al valor deseado, el depurador se detendrá automáticamente. Esto ahorra horas de vida.
Como condición, puede usar no solo una comparación, sino cualquier expresión booleana en C#. Por ejemplo: items[i].IsValid == false && items[i].Id > 100.
Puntos de interrupción con acciones (Tracepoints / Actions)
\n\nA veces no necesita detener la ejecución, sino simplemente registrar un log en un momento determinado. Para eso existen los puntos de interrupción con acciones. Coloque un punto de interrupción, haga clic derecho → «Actions» → ingrese un mensaje como Método llamado con parámetro: {parameter}.
Esto funciona como Console.WriteLine, pero sin modificar el código. Puede mostrar los valores de las variables directamente en la ventana Output, sin interrumpir el funcionamiento de la aplicación.
«Adjuntar al proceso» (Attach to Process)
\n\n¿Alguna vez ha ocurrido que el bug solo se reproduce en producción, pero localmente no? Use Debug → Attach to Process. Puede conectarse a un proceso ya en ejecución (por ejemplo, IIS, Windows Service o una aplicación de escritorio).
Un detalle importante: asegúrese de tener los archivos PDB (símbolos) para la versión del ensamblado que se está ejecutando. De lo contrario, en lugar de nombres de variables, verá Variable1 y Variable2.
Técnicas avanzadas: diagnóstico sin detención
\n\nNo todos los bugs se pueden capturar con el depurador. A veces, detener la ejecución cambia el comportamiento del programa (por ejemplo, debido a los tiempos en código multiproceso). Para estos casos, existen otras técnicas de depuración de C#.
\n\nRegistro con DiagnosticSource y Serilog
\n\nEl System.Diagnostics.Trace incorporado es bueno, pero para proyectos serios es mejor usar registro estructurado. Serilog es uno de los registradores más populares para .NET.
using Serilog;\n\nLog.Logger = new LoggerConfiguration()\n .WriteTo.Console()\n .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)\n .CreateLogger();\n\ntry\n{\n var result = SomeRiskyOperation();\n Log.Information("Operación ejecutada con éxito. Resultado: {Result}", result);\n}\ncatch (Exception ex)\n{\n Log.Error(ex, "Error al ejecutar la operación");\n}\n\nLos registros estructurados permiten buscar errores no por texto, sino por campos. Por ejemplo, puede encontrar todos los registros con OrderId = 12345 de la última hora. Esto es más potente que grep en un archivo de texto.