Exceptions: Não use try-catch-finally

Essa semana estava revendo um trecho do ARDA (https://github.com/DXBrazil/Arda) e os primeiros issues e pull requests estavam relacionados com o tratamento de exceção. Por coincidência, nessa mesma semana, fui perguntado sobre as melhores práticas de Exceptions sobre como e quando usar.

Comecei olhando a documentação do C# sobre o try-catch-finally.

Try-catch-finally
/en-us/dotnet/csharp/language-reference/keywords/try-catch-finally

Nesse artigo, há o seguinte trecho de código:

image

Então, pergunto:

O que há de errado nesse exemplo?

Embora a linguagem permita a construção e a documentação confirma que é possível, sigo o pensamento de que a sintaxe try..catch..finally não deveria existir no C#. Na minha opinião, não se deve usar try-catch-finally. Essa é uma afirmação forte e, antes de que alguém se desespere, deixo explicar meu ponto de vista.

Por que não existe “finally” no C++?

Você sabia que a linguagem C++ suporta exceções e somente a construção try-catch? Não existe finally!

https://www.stroustrup.com/bs_faq2.html#finally
Why doesn't C++ provide a "finally" construct?

Resposta do Bjarne Stroustrup:

Because C++ supports an alternative that is almost always better: The "resource acquisition is initialization" technique (TC++PL3 section 14.4).

A linguagem do C++ adota o padrão de Resource Acquisition is Initialization (RAII) . Em outras palavras, os recursos devem ser desalocados dentro do destrutor das classes, dispensando o uso do finally.

Use try-catch

Quando lidamos com exceção, tratamos as exceptions através de blocos try-catch.

image https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch

Não tem segredo: as exceções são capturadas pelo bloco catch para tratamento do erro.

Use try-finally

A linguagem suporta a construção try-finally para liberar os recursos alocados.

image https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally

Diferente do C++, o garbage collector do C# (.NET) é não-determinístico. Por isso, sempre garanta que os recursos são liberados no momento certo através do finally.

Não use try-catch-finally

A construção try-catch-finally mistura dois propósitos distintos no mesmo código. Por isso, deixe claro a intenção e use a sintaxe adequada com os blocos:

  • try-catch: Tratar os erros e exceções
  • try-finally: Liberar explicitamente os recursos

Reescrevi o trecho anterior usando try-catch-finally e, ao invés de melhorar o código, parece que ficou mais confuso:

image

Nesse momento, é natural encapsular o trecho do código de leitura de arquivo dentro de outra função. Veja como o código fica mais claro:

image

A função ReadFileBlock gerencia o objeto File, fazendo a alocação de recurso e liberando no finally.

image

Compare como ficou muito melhor que a versão try-catch-finally.

 

Conclusão

Nesse artigo, apresentei a ideia de evitar o try-catch-finally porque essa construção mistura os conceitos de tratamento de exceção e liberação de recurso. Ao invés disso, recomendo que use try-catch ou try-finally.