Depuração de código C#: de "por que não funciona?" a "eu sei onde está o erro"
\n\nTodo desenvolvedor C#, mais cedo ou mais tarde, enfrenta uma situação em que o código compila, executa, mas faz algo completamente diferente do esperado. Ou trava com um erro impossível de reproduzir no ambiente de teste. Nesses momentos, a depuração de C# deixa de ser uma tarefa rotineira e se transforma em uma verdadeira arte.
\n\nNeste artigo, vamos explorar técnicas e ferramentas que ajudarão você não apenas a encontrar o bug, mas a fazer isso de forma rápida e eficiente. Não vamos nos limitar ao padrão F5 — falaremos sobre pontos de interrupção avançados, expressões condicionais, análise de memória e até mesmo trabalho com dumps no WinDbg. Vamos nessa!
\n\nTécnicas básicas, mas poderosas, de depuração de C#
\n\nAntes de partir para ferramentas exóticas, vamos garantir que você está extraindo o máximo do Visual Studio. Muitos desenvolvedores usam apenas F9 (definir ponto de interrupção) e F10 (passar por cima), mas isso é um desperdício.
Pontos de interrupção condicionais (Conditional Breakpoints)
\n\nImagine: você tem um loop de 1000 iterações, e o bug aparece apenas na 567ª. Definir um ponto de interrupção e pressionar F5 567 vezes? Não. Use pontos de interrupção condicionais.
\n\nfor (int i = 0; i < 1000; i++)\n{\n // Alguma lógica\n ProcessItem(items[i]);\n}\n\nClique com o botão direito no círculo vermelho do ponto de interrupção → selecione "Conditions" → digite i == 567. Quando o contador atingir o valor desejado, o depurador parará automaticamente. Isso economiza horas de vida.
Como condição, você pode usar não apenas comparações, mas qualquer expressão booleana em C#. Por exemplo: items[i].IsValid == false && items[i].Id > 100.
Pontos de interrupção com ações (Tracepoints / Actions)
\n\nÀs vezes, você não precisa interromper a execução, apenas registrar um log em um determinado momento. Para isso, existem pontos de interrupção com ações. Defina um ponto de interrupção, clique com o botão direito → "Actions" → digite uma mensagem como Método chamado com parâmetro: {parameter}.
Isso funciona como Console.WriteLine, mas sem alterar o código. Você pode exibir valores de variáveis diretamente na janela Output, sem interromper a execução do aplicativo.
"Anexar ao processo" (Attach to Process)
\n\nJá aconteceu de o bug só ser reproduzido em produção, mas não localmente? Use Debug → Attach to Process. Você pode se conectar a um processo já em execução (por exemplo, IIS, Windows Service ou aplicativo desktop).
Um detalhe importante: certifique-se de ter os arquivos PDB (símbolos) para a versão do assembly que está em execução. Caso contrário, em vez de nomes de variáveis, você verá Variable1 e Variable2.
Técnicas avançadas: diagnóstico sem parada
\n\nNem todos os bugs podem ser capturados no depurador. Às vezes, interromper a execução altera o comportamento do programa (por exemplo, devido a tempos em código multithread). Para esses casos, existem outras técnicas de depuração de C#.
\n\nRegistro em log com DiagnosticSource e Serilog
\n\nO System.Diagnostics.Trace embutido é bom, mas para projetos sérios é melhor usar registro em log estruturado. Serilog é um dos loggers mais 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("Operação executada com sucesso. Resultado: {Result}", result);\n}\ncatch (Exception ex)\n{\n Log.Error(ex, "Erro ao executar a operação");\n}\n\nLogs estruturados permitem pesquisar erros não por texto, mas por campos. Por exemplo, você pode encontrar todos os logs com OrderId = 12345 na última hora. Isso é mais poderoso do que grep em um arquivo de texto.