.NET - Dicas para boas práticas de programação - Desempenho


A seguir algumas dicas de boas práticas de programação retirados do : Manual do Desenvolvedor .NET - Microsoft Consulting Services. Resolvi publicar para incentivar a leitura. Então leia...

Dicas de Desempenho - Turbinando o código VB.NET

1- Certifique-se que o código que será colocado em produção seja compilado com a opção de suporte a “debug” desabilitada.

2- Habilite a opção “Enable Optimizations” (menu Configuration Properties | Optimizations page) da caixa de diálogo Project Properties.

3- Sempre que possível, crie métodos que não possam ser reescritos (overridable). Ou seja, não use indiscriminadamente o modificador Overridable. Um método selado (aquele que não pode ser reescrito é, em média, 35% mais rápido) deve ser sempre preferido quando a questão for desempenho.
Exemplo:

Class Teste
' Este método pode ser reescrito
Overridable Sub Procedimento()
   'Código
End Sub

' Método que não pode ser reescrito
Private Sub Interno()
   'Código
End Sub

' Método que não pode ser reescrito

Public Sub Externo()
   'Código
End Sub
End Class


4- Chame métodos diretamente na interface principal da classe. Métodos chamados via interfaces secundárias serão de quatro a cinco vezes mais lentos.

5- Evite repetições de Strings. Em aplicações acessadas por vários usuários, podem existir várias instâncias simultâneas na memória que carregam várias duplicações de string para a memória. Use shared member ou string intern pool para evitar o uso desnecessário de memória e garbage collectors.

Segue abaixo um exemplo de código para a implementação de uma propriedade para string de conexão que faz uso do string intern pool, evitando a duplicação de strings.  Exemplo:
 

' O membro privado
Dim m_ConnString As String

Property ConnectionString() As String
Get
    Return m_ConnString
End Get
Set(ByVal Value As String)
    m_ConnString = String.Intern(Value)
End Set
End Property
 

6- Codifique destrutores implementando um método Finalize para liberar recursos que não são automaticamente gerenciados pelo .NET Framework (exemplos: Clipboard, arquivos, conexões com base de dados, etc) – Em .NET, não existe o evento Terminate; porém, é possível implementar o método Finalize (protected). Esse será automaticamente chamado pelo .NET Framework sempre que o objeto estiver prestes a ser destruído pelo “garbage collector”. Exemplo:

Class TesteObject
' põe o coletor de lixo sobre pressão
Dim dummyArr(1000) As Byte

Sub New()
   OpenClipboard(0)
End Sub

Protected Overrides Sub Finalize()
   ' fecha o clipboard quando finalizar
   CloseClipboard()
   MyBase.Finalize()
End Sub
End Class
 

Outra solução que aumenta ainda mais o desempenho, mas que acarreta um pouco mais de disciplina é implementar o método Dispose através da interface Idisposable. Esse método conterá o mesmo tipo de código que o método Finalize teria e deverá ser chamado diretamente por clientes antes de atribuir nothing ao objeto.  Exemplo:

Class TestObject
    Implements IDisposable

Public Sub Dispose() _
   Implements IDisposable.Dispose
   ' fecha o clipboard
    CloseClipboard()
    ' Não é necessário finalizar este objeto
    GC.SuppressFinalize(Me)
End Sub
  ' o resto do código como era no original...
End Class

O código que deve ser escrito no cliente é:

Dim o As TestObject
Try
   ' cria o objeto
   o = New TestObject()
Finally
   ' roda o código de limpeza final e o destrói
   o.Dispose()
   o = Nothing
End Try
 

7- Ao escrever código que faça uso de classes/objetos que disparam eventos, dê preferência para utilizar a nova técnica disponível em .NET de fazer, em “run time”, a atribuição (link) de um evento a um método chamado quando este for disparado. Note que, com a nova técnica, é possível fazer o link do evento com o método de tratamento (event handler) e desfazê-lo. Exemplo:

Dim o As New TestObject

Sub UseDynamicEvents()
' associa o evento com a procedure local
AddHandler o.TestEvent, AddressOf MyHandler
' usa o objeto (agora MyHandler suporta os eventos)

' ...
' remove o tratador do evento
RemoveHandler o.TestEvent, AddressOf MyHandler
End Sub

Sub MyHandler()
   ' ...rotina que trata o evento aqui.
End Sub

8- Não use o tradicional WithEvents que, embora continue sendo suportado no VB.NET, tem menor desempenho. Exemplo:

 

' Pode ser usada a declaração New com WithEvents
Dim WithEvents obj As New TestObject
Sub obj_TestEvent() Handles obj.TestEvent
   ' ..Trata o evento aqui
End Sub
 

9- Evite disparar exceções desnecessariamente em seu código. Sobretudo ao criar componentes chamados por outros componentes, tente reduzir a probabilidade de uma exceção para zero. Contra-exemplo:

Dim rand As New Random()

Sub CanThrowException(ByVal prob As Double)
   If rand.NextDouble <= prob Then
         Throw New System.Exception()
   End If
End Sub

A rotina acima pode disparar uma exceção e isso deverá ser tratado na camada cliente da seguinte forma:

Dim i As Integer
Dim prob As Double = 0.01
For i = 1 To 1000000
   Try
      CanThrowException(prob) 
   Catch
     ' Não faz nada neste caso
   End Try
Next

Abaixo segue um exemplo de código alternativo que terá um desempenho muito superior:

Function DoesntThrowException( ByVal prob As Double) As Boolean
   If rand.NextDouble <= prob Then
       Return False   ' notifica a falha
   Else
       Return True   ' notifica o sucesso
   End If
End Function
 

Observação:  Não é a inclusão dos operadores Try… Catch que onera o desempenho do código e, sim, o disparar de uma exceção.

10- Ao concatenar strings repetidas vezes, use o objeto StringBuilder (System.Text.StringBuilder). Isso evitará que a cada alteração da string seja alocada um novo bloco de memória.  Exemplo:

’Criando uma lista de 500 numeros separados por virgula
Dim sb As New System.Text.StringBuilder(2000)
Dim i As Integer

For i = 1 To 500
    sb.Append(CStr(i))
     sb.Append(", ")
Next

Dim s As String = sb.ToString ‘armazena o resultado em s

Para máxima performance, instancie o StringBuilder com tamanho igual ou maior que a quantidade máxima de caracteres a ser armazenada e nunca re-aproveite o objeto após a extração da string com o uso do método ToString.

Observação: a antiga técnica de se utilizar uma string grande fixa e usar a função MID em .NET não é mais adequada pois, a cada chamada, é alocada uma nova área de memória.

11- Sempre que possível, dê preferência por usar value types. Porém, evite-os quando existir a necessidade de atribuir um value type a uma variável de objeto (isso causará uma operação de “boxing”). Quando atribuir um objeto a um value type, faça uso da chamada de um método, no objeto, que retorne apenas o valor desejado (isso evitará unboxing).  Lembre-se de que todos os tipos de dados em .NET podem ser agrupados em:

Value types (tipos Numéricos, Datas, char, structures, enum)  ou   Reference types (strings, arrays, classes)

Agora é por em prática para garantir um código mais robusto e elegante.


José Carlos Macoratti