Todos os programadores Pascal conhecem o procedimento Exit()
desde as primeiras versões dos compiladores. Mas será que todos sabem utilizá-lo corretamente?
Photo by Kev Seto on Unsplash
O procedimento Exit()
é utilizado quando queremos sair de um escopo em execução. Esse escopo pode ser uma função, procedimento, método ou até mesmo o próprio programa.
Vamos dizer que um programa console chame um procedimento Execute
:
procedure Execute;
begin
Writeln('1. Passing on this line...');
Exit;
Writeln('2. It will not pass here');
end;
No exemplo acima, somente a informação do primeiro Writeln
será mostrado no console.
Ao sair de um escopo, o programa retorna imediatamente para o escopo anterior (outra função/procedimento/método ou o próprio programa). A única exceção a essa regra é quando há blocos try-finally
. Se Exit()
for chamado dentro de um bloco try-finally
o compilador irá executar o código dentro do finally-end
antes de sair do escopo.
Aqui está outro exemplo:
procedure Execute;
begin
try
Writeln('1. Passing on this line...');
Exit;
finally
Writeln('2. I am still here!');
end;
Writeln('3. It will not pass here');
end;
Os textos #1 e #2 serão mostrados no console. Mesmo que Exit()
tenha sido chamado antes da impressão do texto #2, ainda sim o código é executado devido ao try-finally
.
Outro exemplo de uso do Exit()
é quando fazemos validações. Se uma validação ou checagem não retornar verdadeiro, utilizamos o Exit()
para parar a execução do escopo atual.
Suponha que queremos somar dois números inteiros, mas só queremos números maiores que zero:
function Sum(A, B: Integer): string;
begin
Result := 'Invalid result';
if (A < 0) or (B < 0) then
Exit;
Result := Format('The result is %d', [A + B]);
end;
No exemplo acima, o retorno da função Sum
é inicializado com um valor inválido e depois há uma validação para saber se os valores são menores que 0
. Se o teste falhar, o programa irá retornar para o escopo anterior à chamada da função Sum
com o resultado inválido. Mas se o teste não falhar, o resultado da função será a soma de A e B.
Há aqueles que são adeptos da programação estruturada e preferem não “quebrar” a execução do programa com uma “saída antecipada”, ou seja, não fazem uso do Exit()
por acreditarem que o código ficaria mais simples.
Então vamos reescrever o exemplo anterior:
function Sum(A, B: Integer): string;
begin
if (A > 0) and (B > 0) then
Result := Format('The result is %d', [A + B]);
else
Result := 'Invalid result';
end;
Parece mais simples? Bem, nesse exemplo eu diria que sim. Porém para exemplos com mais condicionais, eu diria que não (vamos ver isso mais abaixo).
E se quiséssemos dizer ao usuário que seus dados não estão corretos?
function Sum(A, B: Integer): string;
begin
Result := 'Invalid result';
if (A > 0) then
begin
if (B > 0) then
Result := Format('The result is %d', [A + B]);
else
Writeln('B should be greater than zero');
end
else
Writeln('A should be greater than zero');
end;
Nesse exemplo não utilizamos Exit()
e eu acho que o código está bem confuso. Os testes estão “separados” do retorno de aviso para o usuário (Writeln
).
Kent Beck , Martin Fowler afirmaram categoricamente que “um ponto de saída não é realmente uma regra útil. A clareza é o princípio chave: se o método for mais claro com um ponto de saída, use um ponto de saída, caso contrário, não”
Então vamos reescrever o exemplo anterior com o uso de Exit()
:
function Sum(A, B: Integer): string;
begin
Result := 'Invalid result';
if (A < 0) then
begin
Writeln('A should be greater than zero');
Exit;
end;
if (B < 0) then
begin
Writeln('B should be greater than zero');
Exit;
end;
Result := Format('The result is %d', [A + B]);
end;
O código ficou um pouco maior, é verdade, porém os testes e avisos para o usuário ficaram mais simples, na minha opinião. Você não precisa acompanhar todos os if-else
aninhados. A cada teste que falhar, o aviso está logo abaixo e o escopo será abortado com o uso do Exit()
. Se todos os testes não falharem, a função irá retornar a soma de A e B.
No Delphi, a partir da versão 2009, o procedimento Exit()
ganhou uma melhoria: Exit()
pode ter um parâmetro especificando um resultado. O parâmetro deve ser do mesmo tipo que o resultado da função.
O FPC também tem a mesma definição, porém não sei quem implementou essa nova feature primeiro.
Então vamos reescrever o exemplo anterior:
function Sum(A, B: Integer): string;
begin
if (A < 0) then
Exit('A should be greater than zero');
if (B < 0) then
Exit('B should be greater than zero');
Result := Format('The result is %d', [A + B]);
end;
Simples e limpo.
Exit()
pode receber como parâmetro qualquer tipo de retorno, até mesmo instâncias de Interfaces. Utilizando esse parâmetro, é como se ganhássemos o mesmo comportamento da palavra reservada return
, em Java. No entanto, Exit()
em conjunto com Result
nos dá ainda mais possibilidades de retorno para as funções.
Até logo.