Um bloco de código entre as expressões Try...Catch são testados e caso de um erro implícito no código (uma exceção), ou erro explicito declarado no programa, tem seu fluxo desviado para o bloco de tratamento de erro em catch.
Diferente do BEGIN SEQUENCE ... END SEQUENCE, onde o código dispara o bloco de erros e pode continuar sua execução sem cair no RECOVER, apenas mediante ao comando o BREAK, uma exceção no código, mesmo que de forma implícita (por exemplo, a utilização de uma classe não definida), irá imediatamente desviar o fluxo de código para o bloco de tratamento de erro.
Outra diferença é que a captura do erro ocorre via a classe ErrorClass(), que pode ou não ter sido definida durante a execução do programa, e conterá em seus parâmetros o conteúdo do erro informado.
Apesar do TRY...CATCH só estar disponível no TLPP, ainda é possível realizar um THROW, implícito ou explicito a partir de uma chamada para uma função em advpl dentro de seu bloco de código. |
TRY ... < instruções > ... [ THROW [ < expressão > ] ] ... < instruções > ... [ RECOVER [ USING < variável > ] ] ... < instruções > ... CATCH < instruções > FINALLY < instruções > ENDTRY |
Nome | Tipo | Descrição | Obrigatório | Referência |
---|---|---|---|---|
TRY | Expressão | Define o inicio do bloco de código contendo o controle de erro | Sim | Não |
THROW [ < expressão > ] | Expressão | Define um desvio explicito para o tratamento de erro | Não | Sim |
CATCH | Expressão | Define um ponto de tratamento de erro e/ou recuperação que será o ponto de desvio em caso de erro no bloco do TRY | Sim | Não |
FINALLY | Expressão | Define um bloco de código que será executado de qualquer maneira(se tiver ou não tiver exceção lançada) | Não | Não |
ENDTRY | Expressão | Define o fim do bloco de sequência | Sim | Não |
Um dos modelos mais simples de exemplo que temos, é onde realizamos um throw explicito em nosso programa. Isso faz com que, indifente do nível do stack onde estamos no nosso programa, ele vai mudar imediatamente o fluxo do programa para o CATCH, e informar a ultima descrição de erro erro (varError:description) informada:
function u_fTryT01() conout("funcao que cria o errorclass") varError := ErrorClass():New() //sempre a exceção lançada deve ser uma instância da classe ErrorClass ou de uma classe que herda de ErrorClass varError:genCode := 15461 varError:description := "ERRO designado no ErrorClass do u_fTryT01" throw varError //lançamento da exceção com objeto de erro conout("NÃO PODE PASSAR NUNCA") //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior return User Function TryC001 Local x Local y := "TryTest" TRY //ErrorClass pode ou não ser declarado no mesmo nível //Local oError := ErrorClass():New() u_fTryT01() conout("essa linha NÃO deve ser executada (throw explicito dentro do u_fTryT01())") x := &y.(,) CATCH oError ConOut( oError:description ) ENDTRY Return( NIL ) |
Uma observação importante ao declarar a ErrorClass em outro nível de stack, é o seu escopo. Observe o exemplo abaixo:
function u_fTryT02p() conout("funcao que cria o errorclass como publica (vai funcionar)") public varError := ErrorClass():New() //sempre a exceção lançada deve ser uma instância da classe ErrorClass ou de uma classe que herda de ErrorClass varError:genCode := 15461 varError:description := "ERRO DO u_fTryT02p" return User Function TryC002 TRY //se eu declarar como local, e nao public, vai dar erro no varerror dentro do catch.. u_fTryT02p() throw varError conout("NÃO PODE PASSAR NUNCA") //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior CATCH varError //ConOut( ProcName() + " " + Str(ProcLine()) + " " + e:description ) ConOut( varError:description ) ENDTRY Return( NIL ) |
Nesse caso o throw explicito foi realizado em outro nivel de stack onde ocorreu a declaração da instancia da ErrorClass. Porem como foi declarado como publica, não houve problema. Diferentemente do exemplo abaixo:
function u_fTryT02() conout("funcao que cria o errorclass como local (NÃO vai funcionar)") varError := ErrorClass():New() //sempre a exceção lançada deve ser uma instância da classe ErrorClass ou de uma classe que herda de ErrorClass varError:genCode := 15461 varError:description := "ERRO DO u_fTryT02" return User Function TryC0002 TRY //se eu declarar como local, e nao public, vai dar erro no varerror dentro do catch.. u_fTryT02() throw varError conout("NÃO PODE PASSAR NUNCA") //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior CATCH varError conout("conout aqui vai mostrar errado, como 'varError nao existe', e nao como 'ERRO DO u_fTryT02'") ConOut( varError:description ) ENDTRY Return( NIL ) |
Aqui o erro capturado será na linha do THROW varError, e não a informação declarada no stack anterior, ja que nesse ponto do stack a instancia varError não existe.
Outro exemplo simples de utilização é com a captura de erros de forma implícita, por conta de exceções na linguagem. Veja o exemplo onde a função TryTest não existe em nosso RPO:
User Function TryC003 Local x Local y := "TryTest" TRY u_fTryT02p() x := &y.(,) conout("NÃO PODE PASSAR NUNCA") //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior CATCH oError ConOut( oError:description ) ENDTRY Return( NIL ) |
Nesse exemplo mesmo com a classe declarada varError na função , o conout da oError será "Macro FC: cannot find function :TryTest". Observe também que apesar de não existir a instancia declara de oError, mesmo assim a chamada de CATCH a essa instancia foi preenchida com a instancia padrão de ErrorClass() que existe no AppServer, isso considerando que o erro foi implícito, e não explicito como na chamada de THROW varError de um exemplo anterior.
Para ficar claro a questão de erros explicitos e implicitos, observe no proximo exemplo onde mesmo informando uma descrição a instancia oError, a informação do conout é do erro implícito:
User Function TryC0003 Local x Local y := "TryTest" TRY oError := ErrorClass():New() oError:genCode := 15461 oError:description := "ERRO designado no ErrorClass do u_TryC0003" x := &y.(,) conout("NÃO PODE PASSAR NUNCA") //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior CATCH oError ConOut( oError:description ) ENDTRY Return( NIL ) |
Quanto a utilização da instancia da classe ErrorClass, atenção a esse comportamento já citado sobre a instancia que existe nativamente do Appserver. Nesse exemplo, a instancia de oSample2 é a ErrorClass, e a instancia do oSample de uma classe que nao existe. No catch foi utilizado a instancia errada, mas como ela não existe, foi criado nela a referencia para a ErrorClass nativa do Appserver. Então teremos dois conouts, o do oSample2 da nossa classe ErrorClass (que não tem nada), e o oSample com a instancia do ErrorClass nativo do Appserver, com a descrição "invalid class NAOEXISTE":
Function u_TryC005 local val2 := 2 oSample2 := ErrorClass():New() try //conout("2" + val2) oSample := naoexiste():New() ConOut("isso NÃO deve executar...") catch oSample //errado, mas como o objeto nao existe, ele recebe o ErrorClass nativo do Appserver ConOut("Estou no Catch...") ConOut( oSample2:description ) conout( oSample:description) conout("atencao para a confusao entre as classes... o oSample2 (que é a classe correta) mas o catch que esta com a instancia errada, funcionou") endtry Return |
Veja um outro exemplo com a classe corretamente informada:
Function u_TryC0005 oSample2 := ErrorClass():New() conout("atencao para a confusao entre as classes...") try oSample := Sample2Class():New() //SampleClassChild():New() //conout("2" + 2) ConOut("Estou no Try...") throw oSample catch oSample2 //as SampleClass ConOut("Estou no Catch...") ConOut( oSample2:description ) endtry Return |
Outra forma de setar na instancia da ErrorClass() nativa do Appserver, é utilizar a função UserException:
user function TryC010() try UserException("qualquer erro para mostrar a questao do throw implicito funcionar via UserException (sem instancia de classe de erro e sem throw explicito)") conout("isso NÃO deve executar") catch e //no catch eh possível nomear a exceção lançada para acessar no corpo do catch conout(e:description) conout(e:genCode) finally //o finally é opcional e será executado de qualquer maneira(se tiver ou não tiver exceção lançada) conout("finally") endtry return |
Outro exemplo importante é que é perfeitamente possivel cruzar a utilização do Comando BEGIN SEQUENCE ... END com o Try..Catch:
function u_fErrBfun03( ) conout("errorblock foi acionado com break") break return Function u_TryCTLPP0002 Local oError Local e TRY //se eu declarar como local, e nao public, vai dar erro no varerror dentro do catch.. deveria resgatar? u_fTryT02p() oError := ErrorBlock( { | e | u_fErrBfun03() } ) Begin Sequence var := var+1 // variavel não existe conout("Isto jamais deve ser executado" ) return .T. Recover conout("Recover, passar aqui") End Sequence conout("terminou o begin..end") throw varError conout( "Isto jamais deve ser executado" ) //nunca passará nesse trecho de código porque tem uma exceção lançada na linha anterior CATCH varError conout("entrou no catch") ConOut( varError:description ) ENDTRY Return |
Application Server 20.3.0.0
<style> div.theme-default .ia-splitter #main { margin-left: 0px; } .ia-fixed-sidebar, .ia-splitter-left { display: none; } table { border: 1px solid; } .syntaxhighlighter { overflow: initial; } #main { padding-left: 10px; padding-right: 10px; overflow-x: hidden; } .aui-header-primary .aui-nav, .aui-page-panel { margin-left: 0px !important; } .aui-header-primary .aui-nav { margin-left: 0px !important; } </style> |