1. Conhecendo o bloco finally
Limpeza e liberação de recursos
Imagina só: você tá fazendo uns experimentos num laboratório de química e, quando termina tudo (ou até se explodiu uns tubos de ensaio), ainda assim precisa arrumar a bancada, lavar os reagentes e apagar a luz. É pra isso que serve o bloco finally — ele sempre roda, depois do try e do catch, não importa se rolou exceção ou não.
try
{
// Aqui vai o código "perigoso" que pode lançar exceção
}
catch
{
// Aqui a gente captura e trata as exceções
}
finally
{
// Esse código sempre vai rodar, teve exceção ou não
}
Pra que serve isso? Principalmente pra garantir que os recursos vão ser liberados: fechar arquivo, conexão com banco de dados, destravar a porta do servidor... Se não tivesse o finally, depois de um erro alguns recursos podiam ficar "presos" — e aí já viu, né? Por exemplo, o arquivo fica bloqueado e nem o admin consegue abrir.
Por que não colocar tudo no catch?
Será que não dava pra liberar o recurso só no catch? Até dava, teoricamente. Mas se tudo der certo, o catch nem roda. Se você quer ter certeza que a liberação do recurso vai acontecer sempre, tem que usar o finally.
Na vida real, tem várias situações em que não importa se deu bom ou ruim — a limpeza é obrigatória. É pra isso que inventaram o finally.
2. Particularidades do bloco finally
Quando o finally NÃO roda?
Pergunta pegadinha! O finally executa sempre. Mesmo se no bloco try rolar um return (saída antecipada do método), ou lançar uma nova exceção, o finally vai rodar de qualquer jeito.
static void Teste()
{
try
{
Console.WriteLine("Antes do return");
return;
}
finally
{
Console.WriteLine("finally vai rodar mesmo assim!");
}
}
//Chamada do Teste()
Teste();
Vai mostrar:
// Antes do return
// finally vai rodar mesmo assim!
Mas se do nada seu app tiver um "crash pesado" (tipo, desligaram o PC, mataram o processo ou o CLR morreu), aí o bloco finally não vai rodar. Mas aí já era, não tem o que fazer.
Operador finally e a pilha de chamadas
A pilha de chamadas a gente vai ver melhor na próxima aula, mas por enquanto pensa nela como uma pilha de métodos chamados, por onde o programa "desce" se não achar um catch que sirva.
Outro ponto importante: se no try rolar uma exceção e no catch ela não for tratada (tipo, não tem handler certo), o programa sai do método atual e vai subindo na pilha de chamadas até achar um catch que sirva. Mas antes disso, sempre vai rodar o bloco finally em cada nível da pilha.
3. Operador throw: como lançar exceções você mesmo
O que é o throw e pra que serve
Às vezes só capturar a exceção não basta — tem hora que você precisa criar seu próprio erro e "lançar" ele pra fora. É pra isso que existe o operador throw.
throw new Exception("Esse é meu erro especial!");
throw basicamente fala pro CLR: "Achei algo muito errado aqui, tô lançando meu Exception, agora quem chamou esse código que se vire".
throw sem criar uma nova exceção
Dá pra usar throw; dentro do bloco catch pra relançar a exceção que acabou de ser capturada — tipo, se você tratou parte do erro, mas quer que o código acima continue lidando com ele.
try
{
OperacaoPerigosa();
}
catch (Exception ex)
{
LogError(ex);
throw; // relança a exceção atual, mantendo a pilha de chamadas
}
Se a gente escrevesse throw ex;, a info da pilha de chamadas ia se perder — isso é má prática.
4. Como o finally funciona junto com o throw?
finally roda até quando lança exceção
Bora ver o que acontece se dentro do try rolar um throw, mas a gente também tem um finally:
try
{
Console.WriteLine("Antes do erro...");
throw new Exception("Erro durante o try!");
}
catch
{
Console.WriteLine("Catch pegou o erro.");
}
finally
{
Console.WriteLine("Finally rodou.");
}
Resultado:
Antes do erro...
Catch pegou o erro.
Finally rodou.
E se não tiver um catch que sirva, o finally ainda assim vai rodar antes do programa cair fora.
Detalhe: o que acontece se no finally também tiver throw?
Se no finally rolar um throw, essa exceção vai substituir a anterior. Ou seja, a info do que rolou no try/catch vai se perder. Por isso não é recomendado lançar throw no finally se já teve exceção antes.
try
{
throw new Exception("Erro no try");
}
finally
{
throw new Exception("Erro no finally");
}
// No fim: só vai aparecer "Erro no finally"
5. Dicas práticas e erros comuns
O que a galera iniciante mais esquece
- Não usar o bloco finally pra liberar recursos, confiando só no catch.
- Colocar código que pode lançar novas exceções dentro do finally — isso gera uns bugs bem chatos.
- Esquecer que o return dentro do try não "pula" o finally — ele sempre vai rodar.
Alternativa ao finally: qual escolher?
Com a chegada do using (que a gente vai ver melhor depois), liberar recursos ficou ainda mais fácil, mas no fundo, o using usa o finally por baixo dos panos. Pra situações fora do padrão (tipo destravar ou mandar mensagem de erro) — ainda tem que usar o finally.
Por que o pessoal adora perguntar de finally em entrevista
Todo recrutador que já escreveu servidor sob carga pesada adora perguntar sobre o finally. Normalmente perguntam: "O que acontece se no try tem um return e no finally um throw?" ou "A liberação de recursos é garantida quando rola exceção?". Agora você não só tem as respostas, mas entende o motivo. Você vai conseguir explicar como o finally funciona, pra que serve, quando usar e por que nenhum código sério vive sem ele.
GO TO FULL VERSION