CodeGym /Cursos /C# SELF /Bloco finally e operador throw

Bloco finally e operador throw

C# SELF
Nível 13 , Lição 2
Disponível

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
}
Estrutura try-catch-finally no C#

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.

Isso garante que os recursos são liberados certinho, até quando rolam erros estranhos.

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!");
Criando e lançando sua própria exceção

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
}
Relançando 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.");
}
finally executa até quando lança exceção

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"
Exceção do finally substitui a anterior

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.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION