Desenvolvimento - Visual Basic .NET

.Net Framework Inside : On Error Resume Next (Anjo ou Demónio)?

On Error Resume Next sempre foi utilizado largamente. Mas, por traz desta facilidade existe ou não um demónio que pode assombrar as aplicações? Este artigo entra no cerne da questão para responder a esta pergunta.

por Guilherme Bacellar Moralez



Falando francamente, eu já fui um programador 100% de VB, e confesso que paguei meus estudos universitários com ASP, VB6 e VB.NET.

Durante meu amadurecimento profissional conheci o C#.NET e uma das coisas que mais me incomodaram foi o fato do C# não possuir a diretiva (On Error Resume Next).

Na minha humilde opinião o (On Error Resume Next) é uma diretiva de emergência em casos de problemas indesejados.

O que isso faz?                                                        

Para responder a esta pergunta, temos de pensar em situações críticas.

Vamos usar um pouco de imaginação e pensar em um login de usuários de uma loja de e-commerce.

O login vai funcionando muito bem até que um dia temos a indesejada tela de erro quando qualquer usuário tenta realizar o login.

Então, temos duas possíveis vertentes de ação:

1. Parar e entender o erro, corrigir o problema e alterar em produção.

2. Dar um jeito.

Mesmo eu admito que, por mais que a 1º opção seja absolutamente correta, em situações tão críticas quanto essas (e elas existem sim!) nós torcemos para que um (On Error Resume Next) funcione.

Então, a primeira coisa que fazemos é tentar usar. Se funcionar é lucro e o problema está resolvido :-)

Porque não usar?

Além do óbvio motivo de que usar uma declarativa destas é admitir que não sabemos programar corretamente e/ou admitir que não conseguimos achar o erro em nossa aplicação, existe a questão da performance.

SIM!!!!!!!!!!!!!!!!!!!!!

Você está lendo certo. Quando usamos o (On Error Resume Next) em uma aplicação VB.NET, mesmo que o código esteja 1000% correto, teremos um GRANDE problema de performance e vou provar isso abaixo.

Como forma de exemplo, utilizaremos uma função de demonstração com vários pontos de erro possíveis, principalmente dentro de um loop:

Function ContaCaracteresNumericos(ByVal entrada As String) As Integer

        " Cria as Variaveis

        Dim qtdCaracteres As Integer = 0

        Dim entradaChar As Char()

        " Quebra a Entrada em Caracteres

        entradaChar = entrada.ToCharArray()

        " Looping nos Elementos para Verificar se TODOS são Numeros

        For i As Integer = 0 To entradaChar.Length - 1

            " Converte para Numero

            Dim tmpNumero As Integer = entradaChar(i).ToString()

            " Verifica se é Maior que Zero

            If (tmpNumero > 0) Then

                " Soma no Contador

                qtdCaracteres = qtdCaracteres + 1

            End If

        Next

        " Retorna Total

        Return qtdCaracteres

    End Function

Na outra ponta, precisamos analisar o código que o compilador do VB.NET gera, então para isso usaremos o sempre útil Reflector.

Public Shared Function ContaCaracteresNumericos(ByVal entrada As String) As Integer

    Dim qtdCaracteres As Integer = 0

    Dim entradaChar As Char() = entrada.ToCharArray

    Dim VB$t_i4$L0 As Integer = (entradaChar.Length - 1)

    Dim i As Integer = 0

    Do While (i <= VB$t_i4$L0)

        If (Conversions.ToInteger(entradaChar(i).ToString) > 0) Then

            qtdCaracteres += 1

        End If

        i += 1

    Loop

    Return qtdCaracteres

End Function

Podemos observar que não há nada de incomum além das otimizações naturais que esperamos de um compilador Microsoft.

Até agora sem segredos, então, passemos a utilizar o (On Error Resume Next):

Function ContaCaracteresNumericos(ByVal entrada As String) As Integer

        " Set Performance = Ruim

        On Error Resume Next

        " Cria as Variaveis

        Dim qtdCaracteres As Integer = 0

        Dim entradaChar As Char()

        " Quebra a Entrada em Caracteres

        entradaChar = entrada.ToCharArray()

        " Looping nos Elementos para Verificar se TODOS são Numeros

        For i As Integer = 0 To entradaChar.Length - 1

            " Converte para Numero

            Dim tmpNumero As Integer = entradaChar(i).ToString()

            " Verifica se é Maior que Zero

            If (tmpNumero > 0) Then

                " Soma no Contador

                qtdCaracteres = qtdCaracteres + 1

            End If

        Next

        " Retorna Total

        Return qtdCaracteres

    End Function

E o código gerado?

Public Shared Function ContaCaracteresNumericos(ByVal entrada As String) As Integer

    Dim ContaCaracteresNumericos As Integer

    Dim VB$ResumeTarget As Integer

    Try

        Dim VB$CurrentStatement As Integer

    Label_0001:

        ProjectData.ClearProjectError

        Dim VB$ActiveHandler As Integer = -2

    Label_000A:

        VB$CurrentStatement = 2

        Dim qtdCaracteres As Integer = 0

    Label_000F:

        VB$CurrentStatement = 3

        Dim entradaChar As Char() = entrada.ToCharArray

    Label_0019:

        VB$CurrentStatement = 4

        Dim VB$t_i4$L0 As Integer = (entradaChar.Length - 1)

        Dim i As Integer = 0

        goto Label_005C

    Label_0027:

        VB$CurrentStatement = 5

        Dim tmpNumero As Integer = Conversions.ToInteger(entradaChar(i).ToString)

    Label_003D:

        VB$CurrentStatement = 6

        If (tmpNumero <= 0) Then

            goto Label_0053

        End If

    Label_004B:

        VB$CurrentStatement = 7

        qtdCaracteres += 1

    Label_0053:

        VB$CurrentStatement = 9

        i += 1

    Label_005C:

        If (i <= VB$t_i4$L0) Then

            goto Label_0027

        End If

    Label_0065:

        VB$CurrentStatement = 10

        ContaCaracteresNumericos = qtdCaracteres

        goto Label_0102

    Label_0079:

        VB$ResumeTarget = 0

        Select Case (VB$ResumeTarget + 1)

            Case 1

                goto Label_0001

            Case 2

                goto Label_000A

            Case 3

                goto Label_000F

            Case 4

                goto Label_0019

            Case 5

                goto Label_0027

            Case 6

                goto Label_003D

            Case 7

                goto Label_004B

            Case 8, 9

                goto Label_0053

            Case 10

                goto Label_0065

            Case 11

                goto Label_0102

            Case Else

                goto Label_00F7

        End Select

    Label_00B7:

        VB$ResumeTarget = VB$CurrentStatement

        Select Case IIf((VB$ActiveHandler > -2), VB$ActiveHandler, 1)

            Case 0

                goto Label_00F7

            Case 1

                goto Label_0079

        End Select

    Catch obj1 As Object When (?)

        ProjectData.SetProjectError(DirectCast(obj1, Exception))

        goto Label_00B7

    End Try

Label_00F7:

    Throw ProjectData.CreateProjectError(-2146828237)

Label_0102:

    If (VB$ResumeTarget <> 0) Then

        ProjectData.ClearProjectError

    End If

    Return ContaCaracteresNumericos

End Function

Analisando o código gerado com um pouco mais de atenção, podemos observar que, além da quantidade de linhas de código ter aumentado consideravelmente (o que é óbvio), o loop desapareceu e foi trocado por instruções GOTO e todas as linhas de código ganharam overhead de código (outras linhas).

Aqueles que desejam ter o deleite de analisar o resultado com C#, segue o código abaixo:

public static int ContaCaracteresNumericos(string entrada)

{

    int ContaCaracteresNumericos;

    int VB$ResumeTarget;

    try

    {

        int VB$CurrentStatement;

    Label_0001:

        ProjectData.ClearProjectError();

        int VB$ActiveHandler = -2;

    Label_000A:

        VB$CurrentStatement = 2;

        int qtdCaracteres = 0;

    Label_000F:

        VB$CurrentStatement = 3;

        char[] entradaChar = entrada.ToCharArray();

    Label_0019:

        VB$CurrentStatement = 4;

        int VB$t_i4$L0 = entradaChar.Length - 1;

        int i = 0;

        goto Label_005C;

    Label_0027:

        VB$CurrentStatement = 5;

        int tmpNumero = Conversions.ToInteger(entradaChar[i].ToString());

    Label_003D:

        VB$CurrentStatement = 6;

        if (tmpNumero <= 0)

        {

            goto Label_0053;

        }

    Label_004B:

        VB$CurrentStatement = 7;

        qtdCaracteres++;

    Label_0053:

        VB$CurrentStatement = 9;

        i++;

    Label_005C:

        if (i <= VB$t_i4$L0)

        {

            goto Label_0027;

        }

    Label_0065:

        VB$CurrentStatement = 10;

        ContaCaracteresNumericos = qtdCaracteres;

        goto Label_0102;

    Label_0079:

        VB$ResumeTarget = 0;

        switch ((VB$ResumeTarget + 1))

        {

            case 1:

                goto Label_0001;

            case 2:

                goto Label_000A;

            case 3:

                goto Label_000F;

            case 4:

                goto Label_0019;

            case 5:

                goto Label_0027;

            case 6:

                goto Label_003D;

            case 7:

                goto Label_004B;

            case 8:

            case 9:

                goto Label_0053;

            case 10:

                goto Label_0065;

            case 11:

                goto Label_0102;

            default:

                goto Label_00F7;

        }

    Label_00B7:

        VB$ResumeTarget = VB$CurrentStatement;

        switch (((VB$ActiveHandler > -2) ? VB$ActiveHandler : 1))

        {

            case 0:

                goto Label_00F7;

            case 1:

                goto Label_0079;

        }

    }

    catch (object obj1) when (?)

    {

        ProjectData.SetProjectError((Exception) obj1);

        goto Label_00B7;

    }

Label_00F7:

    throw ProjectData.CreateProjectError(-2146828237);

Label_0102:

    if (VB$ResumeTarget != 0)

    {

        ProjectData.ClearProjectError();

    }

    return ContaCaracteresNumericos;

}

A que conclusão chegamos?

Vou encerrando o artigo por aqui e fica uma reflexão:

“Vale a pena tudo isso pela comodidade do código continuar a executar mesmo dando erro?”

Guilherme Bacellar Moralez

Guilherme Bacellar Moralez - Bacharel em Ciências da Computação, Desenvolvedor .NET há 4 anos, MCAD, Autor de Livros e Artigos, Arquiteto de Software do Projeto D.NET (Framework de Desenvolvimento .NET).
Blog:
http://dotnetmax.bacellar.org/