Árvore de páginas

TLPP - LANGUAGE

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.


01. SINTAXE

TRY          ...
          < instruções >
          ...
          [ THROW [ < expressão > ] ]
          ...
          < instruções >
          ...
CATCH
          < instruções >
FINALLY
          < instruções >
ENDTRY


02. PARÂMETROS

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ãoDefine um desvio explicito para o tratamento de erroNãoSim
CATCHExpressãoDefine um ponto de tratamento de erro e/ou recuperação que será o ponto de desvio em caso de erro no bloco do TRYSimNão
FINALLYExpressãoDefine um bloco de código que será executado de qualquer maneira(se tiver ou não tiver exceção lançada)NãoNão
ENDTRYExpressãoDefine o fim do bloco de sequênciaSimNão



03. EXEMPLOS

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 questão 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





04. ABRANGÊNCIA

Application Server 20.3.0.0



05. VEJA TAMBÉM




  • Sem rótulos