Técnicas de depuración en C#
Aprende técnicas de depuración en C# con breakpoints y herramientas de análisis para detectar y corregir errores.
En el proceso de desarrollo de software, la depuración (debugging) es un proceso sistemático para analizar el comportamiento inesperado de un programa e identificar el código defectuoso. En C#, herramientas como Visual Studio y Visual Studio Code ofrecen potentes capacidades de depuración que permiten a los desarrolladores ejecutar el código paso a paso, inspeccionar valores de variables y analizar el flujo de ejecución.
Diferencia entre los modos Debug y Release
Los proyectos .NET tienen dos configuraciones principales de compilación:
- Debug: Se utiliza durante el desarrollo. Contiene símbolos y metadatos adicionales que facilitan la depuración.
- Release: Se usa para la versión final del producto. Se aplican optimizaciones y se eliminan los símbolos de depuración.
// En Visual Studio, seleccionable en la barra superior:
// [Debug ▼] → [Release]
En modo Debug, el programa puede ejecutarse línea por línea, lo que permite un control total sobre el flujo del código.
Puntos de interrupción (Breakpoints)
Un breakpoint es un marcador que detiene la ejecución del programa en una línea específica. De esta manera, el programa se ejecuta hasta ese punto, permitiendo inspeccionar los valores actuales de las variables.
class Program
{
static void Main()
{
int a = 5;
int b = 0;
int c = a / b; // Error: DivideByZeroException
Console.WriteLine(c);
}
}
En el ejemplo anterior, se puede colocar un breakpoint en la línea int c = a / b;
para que el programa se detenga ahí y se puedan examinar los valores de a y b en la ventana “Locals”.
Comandos de ejecución paso a paso (Stepping)
Visual Studio ofrece varios atajos de teclado para ejecutar el código paso a paso:
- F10 – Step Over: Ejecuta la línea actual sin entrar en los métodos llamados.
- F11 – Step Into: Entra en el método llamado en la línea actual.
- Shift + F11 – Step Out: Sale del método actual.
- F5 – Continue: Continúa la ejecución hasta el siguiente breakpoint.
Estas herramientas proporcionan un control completo del flujo del programa y permiten identificar con precisión dónde comienza el error.
Observación de variables (Watch y Autos)
Visual Studio permite monitorear los valores de las variables en tiempo real durante la depuración:
- Ventana Watch: Muestra las variables agregadas manualmente para su observación.
- Ventana Locals: Enumera automáticamente todas las variables en el ámbito actual.
- Ventana Autos: Muestra las variables relacionadas con las últimas líneas ejecutadas.
Al colocar el cursor sobre una variable, aparece una ventana emergente con su valor actual.
Uso de las ventanas Immediate y Watch
La ventana Immediate permite ejecutar código directamente durante la depuración. Con ella, puedes modificar valores de variables o probar expresiones sin reiniciar el programa.
// Ejemplo de la ventana Immediate:
// ? a + b
// ? myList.Count
// a = 25
Esto te permite probar cálculos o ajustar valores sin detener la aplicación.
Puntos de interrupción condicionales
A veces, no quieres que el programa se detenga en cada iteración, sino solo cuando se cumple una condición específica. Para ello se utilizan los breakpoints condicionales.
for (int i = 0; i < 100; i++)
{
Console.WriteLine(i);
}
Después de agregar un breakpoint en este bucle, abre el menú “Conditions” y define la condición
i == 50. El programa se detendrá únicamente cuando i sea igual a 50.
Configuración de excepciones
Desde el menú Debug → Windows → Exception Settings puedes especificar los tipos de excepciones
para los que el programa debe detenerse.
Por ejemplo, puedes configurarlo para detenerse solo en NullReferenceException o InvalidOperationException.
Esta función ayuda a evitar interrupciones innecesarias en aplicaciones complejas.
Depuración dentro de bloques Try / Catch
Incluso cuando los errores se capturan dentro de un bloque try / catch,
Visual Studio puede detenerse en la línea exacta donde se produce la excepción si la opción
“Break on User-Unhandled Exceptions” está habilitada.
try
{
int[] arreglo = new int[3];
arreglo[5] = 10; // IndexOutOfRangeException
}
catch (Exception ex)
{
Console.WriteLine("Excepción capturada: " + ex.Message);
}
Esto te permite examinar el estado de la aplicación justo antes de que se produzca la excepción.
Uso de Debug.WriteLine()
El método Debug.WriteLine() escribe mensajes informativos en la ventana de salida (Output Window) durante la depuración.
Es útil para registrar información en segundo plano sin alterar la salida de la consola.
using System.Diagnostics;
class Program
{
static void Main()
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"Paso {i} completado.");
}
Console.WriteLine("Programa finalizado.");
}
}
Estos mensajes solo son visibles en modo Debug y no se ejecutan en modo Release.
Registro (Logging) y seguimiento de errores
La depuración no solo sirve para el análisis en tiempo real, sino también para el seguimiento a largo plazo de una aplicación.
Para ello, se pueden utilizar bibliotecas de registro como Serilog, NLog o Microsoft.Extensions.Logging.
using Microsoft.Extensions.Logging;
class Program
{
static void Main()
{
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<Program>();
try
{
int a = 10, b = 0;
int c = a / b;
}
catch (Exception ex)
{
logger.LogError(ex, "¡Se ha producido un error!");
}
}
}
El registro permite rastrear errores no solo durante el desarrollo, sino también en entornos de producción.
Ejemplo: Depuración de inicio de sesión de usuario
El programa se detendrá en la línea donde coloques el punto de interrupción (breakpoint).
El siguiente ejemplo muestra cómo depurar paso a paso un error en un formulario de inicio de sesión.
class LoginService
{
public bool Login(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
throw new ArgumentException("El nombre de usuario o la contraseña no pueden estar vacíos.");
return username == "admin" && password == "1234";
}
}
class Program
{
static void Main()
{
var service = new LoginService();
Console.Write("Nombre de usuario: ");
string? u = Console.ReadLine();
Console.Write("Contraseña: ");
string? p = Console.ReadLine();
try
{
bool result = service.Login(u, p);
Console.WriteLine(result ? "Inicio de sesión exitoso" : "Credenciales inválidas");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Puedes colocar un punto de interrupción en cualquier línea e inspeccionar las variables paso a paso.
Al colocar un punto de interrupción en la línea return, puedes inspeccionar los valores de username y password en tiempo real
y analizar por qué la condición devuelve false.
Mejores prácticas
- No encierres cada línea en un bloque
try / catch— maneja las excepciones en capas específicas (por ejemplo, UI o capa de servicios). - Utiliza
Debug.WriteLineo un framework de registro en lugar deConsole.WriteLine. - Usa “Step Over” en lugar de “Step Into” para evitar detalles innecesarios.
- Utiliza puntos de interrupción condicionales para enfocarte en escenarios específicos.
- Usa técnicas de simulación de excepciones para probar el manejo de errores (por ejemplo, entradas inválidas, valores nulos).
TL;DR
- Depuración: El proceso de seguir paso a paso la ejecución del programa para encontrar errores.
- Punto de interrupción (Breakpoint): Detiene la ejecución del código en una línea específica.
- Watch & Immediate: Herramientas para observar variables y ejecutar código al instante.
- Debug.WriteLine: Salida de registro específica para desarrolladores (solo en modo Debug).
- Registro (Logging): Registra errores y eventos, útil en entornos de producción.
- Puntos de interrupción condicionales: Detienen el depurador solo bajo ciertas condiciones, mejorando la eficiencia.
Artículos relacionados
Consejos de Visual Studio / VS Code para C#
Aprende consejos de Visual Studio y VS Code para C# y mejora tu productividad con atajos y herramientas.
Manejo de excepciones en C# (try, catch, finally)
Aprende a manejar excepciones en C# usando bloques try, catch y finally para gestionar errores de forma segura con ejemplos.
Reflexión y enlace tardío en C#
Aprende Reflection y late binding en C# para inspeccionar tipos en tiempo de ejecución y crear aplicaciones dinámicas.