Escrito em August 27th, 2008 as 8:39 am por Guilherme Bacellar

0 Comentários

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

Porque não usar?

Além do obvio 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?”

Posts Relacionados:

  1. .Net Framework Inside : Otimizações do Compilador
  2. .Net Framework Inside : Enumeradores (Enums)
  3. .Net Framework Inside : Strings (System.String)
  4. Ordenação de Listas (Coleções)
  5. Reflection Parte 2 – Construtores
, ,

Be the first to start a conversation

Deixa uma Resposta

znjdb32s6g