Dev (Back & Front)ARTIGO

.Net Framework Inside: On Error Resume Next (amigo ou inimigo)?

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 certo 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:

    Parar e entender o erro, corrigir o problema e alterar em produção.
    Dar um jeito.

Mesmo eu admito que, por mais que a 1º opção seja absolutamente correta, em situações tão críticas quando 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 🙂

Por que 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 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 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 é obvio), 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?”

é Bacharel em Ciências da Computação, Desenvolvedor .NET há 6 anos, MCAD, Autor de Livros e Artigos, Engenheiro de Segurança de Internet Banking e membro da Kontrolu. WebSite: http://www.kontrolu.com.br

Ver perfil