Sie sind auf Seite 1von 22

Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.

90

Greg Reddick

Os objetivos destas Convenções são os de tornar o código:

• Mais legível: para permitir que um leitor compreenda o significado do código com
menos esforço.
• Mais facilmente atualizado: O código pode ser alterado de forma mais confiável para
corrigir erros e aprimorar a sua funcionalidade.
• Mais confiável: O código desempenhará mais proximamente daquilo que é esperado.
• Mais eficiente: O código executa mais rápido ou consume menos recursos.

A versão atual destas Convenções sempre pode ser encontrada no website da Xoc
Software: http://www.xoc.net.

Estas recomendações são carregadas de maior racionalidade do que aquelas fornecidas


nas Convenções de nomenclatura RVBA. Na maioria dos casos existem boas razões
para o que aqui está escrito. No entanto, em alguns deles, uma decisão arbitrária foi
tomada para selecionar uma determinada convenção de um conjunto de alternativas
razoáveis. Outras alternativas, igualmente razoáveis para utilização, estão colocadas
entre chaves {} no final de cada seção.

Nenhum conjunto de Convenções consegue cobrir todos os casos ou considerar todas as


possíveis soluções. A regra geral é que as exceções (portanto, não contempladas
Convenções) possam ser feitas com a aprovação da equipe de programação, depois de
cuidadosa avaliação. As seções são listadas em ordem alfabética para facilitar a sua
utilização como uma obra de referência não representando, portanto, significância de
importância no contexto destas Convenções.

Arrays (Matrizes)

Sempre especifique os dois limites de uma matriz: LBound (o mínimo) e UBound (o


máximo). Isso assegura explicitamente quando o elemento zero da matriz é um elemento
válido ou não. Por exemplo:

Dim astrValue (1 To 10) As String

Por convenção, a variável de índice utilizada para acessar cada item de um Array deve
ser sempre um tipo de dados Long. Isto assegura que, se o tamanho da matriz chegar a
ultrapassar 32.767 elementos, teremos a certeza de que será possível acessar todos os
elementos desse Array. Ao acessar itens de um Array, use sempre as funções LBound
e UBound para determinar o início e fim do loop para acesso a cada item. Isso faz com
que tenhamos certeza de que cada item na matriz é acessível. Por exemplo:

Dim iastrValue As Long


For iastrValue = LBound(astrValue) To UBound(astrValue)
MsgBox astrValue(iastrValue)
Next iastrValue

Assertions

O VBA fornece um mecanismo de asserção através da utilização do comando


Debug.Assert. Se a expressão após a linha Debug.Assert é avaliada como True, o

1
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

código continua. Se, ao contrário, a expressão for avaliada como False, o VBA entra
Modo de Interrupção, tal como se um ponto de interrupção estivesse definido nessa linha.
A linha mostrada no exemplo a seguir funciona como um ponto de interrupção codificado:

Debug.Assert False

Asserções só se aplicam a depuração dentro do ambiente VBA. O VBA não remove


chamadas de função, porque elas podem produzir efeitos colaterais, mas descarta o
retorno valor da função. Sempre que houver uma suposição acerca dos possíveis valores
que ocorram em determinada parte do código, idealmente deve haver uma asserção que
a aponte. Por exemplo, suponha que um procedimento inclua o seguinte código:

Select Case intValue


Case 1
MsgBox "Aircraft"
Case 2
MsgBox "AutoMobile"
Case 3
MsgBox "SnowMobile"
End Select

Este código assume que o valor de intValue sempre estará entre 1 e 3. No entanto, se
por algum bug, intValue receba o valor de 0 ou 4, por exemplo, este código não
funcionará corretamente. O resultado é que a MsbBox não aparece em todas as
ocorrências de intValue. Debugar o código procurando a razão pela qual a MsgBox
não apareceu pode ser demorado. Em vez disso, o código poderia ser escrito de uma
destas duas maneiras:

Debug.Assert intValue >= 1 And intValue <= 3


Select Case intValue
Case 1
MsgBox "Aircraft"
Case 2
MsgBox "AutoMobile"
Case 3
MsgBox "SnowMobile"
End Select

…ou…

Select Case intValue


Case 1
MsgBox "Aircraft"
Case 2
MsgBox "AutoMobile"
Case 3
MsgBox "SnowMobile"
Case Else
Debug.Assert False
End Select

Em geral, cada bloco Select / End Select deve ter um Case Else funcionando
como uma arapuca para lidar com valores inesperados que, no caso, intValue possa
assumir. Se “temos a certeza” de que o Case Else nunca irá ocorrer, é boa prática de
programação colocar uma declaração Debug.Assert False nessa parte do bloco. Se
2
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

o código for corretamente escrito para lidar com a faixa de valores de 1 a 3, mas 0 e 4
também são permitidos, o código deve ser escrito com um comentário no bloco Case
Else para indicar que isto é esperado, como no exemplo:

Select Case intValue


Case 1
MsgBox "Aircraft"
Case 2
MsgBox "AutoMobile"
Case 3
MsgBox "SnowMobile"
Case Else
'Do nothing
End Select

Debug.Assert identifica erros de lógica mais rapidamente. Ao invés de esperar para


que o bug apareça na frente do usuário, ele lhe exibe o feedback imediato de que o bug
ocorreu. Asserções só são eficazes se estiverem presentes, o que significa que elas têm
de ser adicionadas ao escrevermos o código. Qualquer erro de lógica que é acertado no
código é uma boa indicação de que há a necessidade de colocação de algumas
asserções adicionais.

Comentários

Um comentário em VBA começa com um apóstrofo, seguido do texto, e termina no último


caractere digitado na mesma linha. Comentários podem ser colocados sozinhos numa
linha ou logo após o final de uma linha de comando, sempre começando com o apóstrofo
seguido imediatamente pelo texto, preferencialmente, sem espaço entre os dois.

O comentário só deve ser colocado após o final de uma linha de comando em algumas
situações, tais como:
• No final de uma linha de declaração;
• Nas linhas Case de um Select/End Select, e
• Na linha que termina um bloco específico de código para indicar que esse bloco
terminou ali, naquele ponto como, por exemplo, num bloco If/End If que se estenda
por mais de uma página.

Exemplos de Comentários:

Dim dateUTC As String 'time in Universal Coordinated Time


Case 11 'Division by Zero

Se a linha de comentário vier a ter mais de 80 caracteres, que é o limite de caracteres em


uma única linha no VBE, continue escrevendo o comentário na próxima linha, sempre
adicionando um apóstrofo no seu início:

Case 35602 'This key is already associated with an element of this


'collection
Set nodChild = tvw.Nodes.Item(cci.Guid)

Todo comentário deve ser colocado em uma linha separada, e acima da linha a que ele
se refira, observando o mesmo nível de indentação. Um comentário desse tipo é
3
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

geralmente precedido por uma linha em branco, a menos que seja a primeira linha de um
bloco recuado:

vt = vti.VarType

'Special hack for analyzing my code


If LCase$(Left$(strParamName, Len(strcDecPrefix))) = _
strcDecPrefix Then
strDataType = strDecimal
End If

Se o comentário é a primeira linha de um bloco recuado, não deve ser precedido por uma
linha em branco:

If mboolShowProperties Then
'Show properties for each member
For Each mi In ci.Members

Os comentários devem explicar o porquê da colocação daquela linha do código, e não,


como ele executa a tarefa. Este é um exemplo de um comentário inútil:

'Place the VarType into the vt variable


vt = vti.VarType

Fica pior do que não possuir nenhum comentário. O comentário fica totalmente
descabido, se simplesmente alterarmos o nome da variável, de vt para vtCur. Ao se
deparar com um comentário que não corresponde ao que há no código, fica a questão:
“quem é que está certo e quem é que está errado?”. Nessa situação, normalmente o
comentário é que estará errado, mas pode levar algum tempo para se chegar a esta
conclusão. Um comentário inadequado é irremediavelmente pior do que não haver
nenhum comentário, como também é inútil um comentário que diga a mesma coisa que a
linha seguinte de código. Em geral, não escreva um comentário em que seja necessária
uma manutenção, porque no mundo real, com certeza, não será dada essa manutenção.
Por outro lado, um comentário que esclarece a intenção da execução do código pode ser
útil. Por exemplo:

'Store VarType for recovery in error condition.


vt = vti.VarType

Na prática utilize um comentário apenas quando o funcionamento ou comportamento


daquele trecho de código não seja imediatamente perceptível apenas lendo-o. Tenha
como boa prática de programação se esforçar para tornar o código autoexplicativo,
através do uso de boas Convenções, tanto de nomenclatura quanto de codificação.

Constantes

Sempre declare explicitamente o tipo de dados de uma Constante. Por exemplo:

Private Const DblcPi As Double = 3,14159265358979

Se há um valor literalmente escrito no código, deve-se considerar sua substituição por


uma definição explicita de uma Constante, de modo a tornar o código mais legível.
Substituir um “número mágico” utilizado mais de uma vez no código com uma constante é

4
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

boa prática. Constantes globais são permitidas e devem ser utilizadas. Substitua
conjuntos de constantes do tipo de dados Long com o recurso Type tipos usando Enum:

Public Enum PSF


PSF_CHECKDISPLAYASICON = &H8&
PSF_DISABLEDISPLAYASICON = &H10&
PSF_HIDECHANGEICON = &H20&
PSF_NOREFRESHDATAOBJECT = &H80&
PSF_SELECTPASTE = &H2&
PSF_SELECTPASTELINK = &H4&
PSF_SHOWHELP = &H1&
PSF_STAYONCLIPBOARDCHANGE = &H40&
End Enum

Funções de Data e Variáveis do tipo Data

Tenha cuidado ao usar as funções de data do VBA: Date, DateAdd, DatePart,


DateDiff, DateSerial, DateValue, e etc. Estas funções retornam uma variável do
tipo Variant que contém uma data. Se a conversão implícita de retorno é o tipo de
dados String, a representação de string da data exibirá um número do ano contendo
apenas dois dígitos. Evidentemente isto não é o ideal, com o risco de criarmos um “bug
do ano 2000”. Isso vale também para o contrário: converter uma variável do tipo Date
para uma do tipo String. Em vez disso, use a função Format$ para converter a variável
do tipo Date em uma do tipo String. Por exemplo:

strValue = Format$(Date, "dd/mm/yyyy")

Propriedades padrão

Atribuindo ou lendo valores das propriedades padrão de objetos, sem explicita-las no


código, torna-o mais difícil de ser interpretado pelo programador. O VBA permite que você
utilize, por exemplo, o nome de uma TextBox e retorne, apenas com essa menção, o
valor (padrão) de sua propriedade Text:

MsgBox txtValue

Isto exibe o valor da propriedate Text do TextBox txtValue. Traz muito mais clareza
ao código quando o escrevemos assim:

MsgBox txtValue.Text

Para ser ainda mais explícito, poderia até mesmo ser escrito assim:

MsgBox Me.txtValue.Text

A palavra reservada Me representa, nesse caso, um formulário UserForm do VBA, sendo


que esse exemplo de código está, obrigatoriamente, alojado no corpo desse UserForm.

Isso, no entanto, não acrescenta qualquer valor, pois todas as referências a um controle
em um módulo de um formulário são implicitamente relativas ao próprio formulário (Me). A
razão de se preferir o modo explícito de menção é a de evitar que o programador ter que
5
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

descobrir qual propriedade está sendo referenciada. Isto é especialmente útil ao


fazermos referência a controles ActiveX e a DLLs ou a ActiveX, onde as propriedades
padrão são, muitas vezes, desconhecidas. Por exemplo, quando um campo ADO é
referenciado, é permitido escrever desta forma:

varValue = rst!strFirstName

A variável varValue será carregada com o valor da propriedade Value do campo


strFirstName atualmente carregado no RecordSet rst. No entanto, é muito mais
claro escrever assim:

varValue = rst.Fields.Item("strFirstName").Value

Este código não utiliza nenhuma das propriedades padrão dos objetos envolvidos, porém,
recupera o mesmo valor.

Recursos a serem evitados

Evite o uso de recursos do Visual Basic ainda existentes somente para fazer face à
compatibilidade com suas versões anteriores. Evite o uso de características não
documentadas. Além disso, evite o uso de funcionalidade antiga, quando há outra, mais
moderna. Alguns exemplos destes tipos de recursos (antigos, e que devem ser evitados):

 %, &, $, etc, em declaração de variáveis;


 Declarações Rem (prefira fazer o sinal de comentário ‘)
 Numeração de linhas (exceto quando em conjunto com a função Erl em
situações especiais de rotinas de manipulação de erro)
 Linha Única If/Then (use blocos If/End If). Por exemplo, não use:
If boolValue Then MsgBox "Hi There"
 Loop While/Wend (substitua-o por um Do While/Loop, ou por um Do/Loop
Until)
 Variáveis declaradas como Global (prefira o uso de Public)
 Usar Dim na seção de Declarações Gerais (prefira a declaração Private)
 Usando tipos definidos pelo usuário (Type/End Type), exceto no caso de
chamadas de API do Windows ou lendo arquivo de largura fixa (prefira o emprego
de Módulos de Classe)
 GoSub (o desvio de execução do código com o GoSub pode se tornar uma “Torre
de Babel” e deixar o código incompreensível, ou mesmo, não-solucionável)
 A declaração End. Na maioria dos casos basta descarregar o formulário
(Unload).

Desambiguação

Ao fazer referência a classes de uma biblioteca de ActiveX, use sempre o nome da


biblioteca, dizendo explicitamente ao VBA a partir de qual biblioteca se deseja obter a
Classe. Se você não fizer isso o VBA utilizará a ordem das bibliotecas contidas na caixa
de diálogo de referências para determinar, por ele mesmo, de qual biblioteca será
extraída a Classe. O nome da biblioteca sempre aparece na caixa de listagem superior
esquerda do Pesquisador de Objetos do VBA (tecle F2 para exibi-lo, se for o caso). Essa
caixa de listagem, quando ainda não acessada, exibe a expressão “<Todas as
6
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

bibliotecas>”. Por exemplo, se há referências tanto para o Access quanto para o Excel
nas bibliotecas de objeto de determinado projeto, então esta declaração se torna
ambígua:

Dim appObj As Application

Porque tanto a biblioteca de objetos do Access quanto a do Excel possuem uma classe
Application. A classe Application referenciada assumirá o objeto Application
daquela que estiver hospedando o código ou, na falta de um objeto Application, assumirá
o objeto Application daquela referência que primeiro aparecer na listagem. Para evitar
isto, a variável deve ser declarada assim:

Dim appObj As Excel.Application

A Microsoft refere-se a isso como um ato de "desambiguação". Com esta declaração, não
importa a ordem de listagem das bibliotecas na caixa de diálogo Referências, uma vez
que appObj, neste exemplo, sempre fará referência ao objeto Application do Excel.
Assim, tenha por padrão a declaração completa (nome da biblioteca + “.” + nome da
classe) para fins de desambiguação.

Outros exemplos que precisam ser explicitamente declarados para evitar a ambiguidade:

Excel.Range e Word.Range;
Excel.CommandBars e Word.CommandBars;
Excel.Chart e Word.Chart.

Funções “Dollar Sign” ($)

Se o resultado de uma Function é usado como uma String ou atribuído a uma


variável do tipo String, use a forma $ da função. Isso resulta em um código mais
rapidamente executado, porque a conversão de uma variável do tipo Variant para outra
do tipo String é desnecessária. Por exemplo, esta é a maneira adequada para
utilizarmos a forma $ das funções:

If LCase$(Left$(strParamName, Len(strcDecPrefix))) = strcDecPrefix


Then

Este exemplo chama o LCase$ e funções Left$ em vez das funções LCase e Left,
porque o resultado será certamente utilizado como uma String. Se o resultado for
utilizado como uma Variant então, neste caso, deve-se chamar as funções padrão
LCase e Left.

A forma $ da função retorna o mesmo valor quando utilizada a versão Variant. Uma
exceção à regra é a função Date. A função Date deve ser sempre usada assim porque a
função Date com a forma $ não se comporta corretamente. Date$ sempre retorna
informações no formato “mm-dd-yyyy” independentemente das Configurações Regionais
do Windows, enquanto que a função Date as utiliza.

Tratamento de erros

7
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Um procedimento deve sempre incluir o tratamento de erros em tempo de execução. Em


geral, a manipulação de erro sempre deve tratada “por fora” em cada procedimento,
como mostrado neste exemplo:

Private Sub Test()


On Error GoTo ErrorHandler
'Code for the procedure goes here
ExitProcedure:
On Error Resume Next
'Cleanup code for the procedure goes here
Exit Sub
ErrorHandler:
Select Case Err.Number
'Case statements for expected errors goes here
Case Else
Call UnexpectedError(Err.Number, Err.Description, _
Err.Source, Err.HelpFile, Err.HelpContext)
End Select
Resume ExitProcedure
End Sub

Use os nomes de rótulo como mostrado no exemplo, embora tenham sido arbitrariamente
escolhidos. Perceba que os rótulos ExitProcedure e ErrorHandler são alinhados à
extrema esquerda, tornando-os mais visíveis. Quando do tratamento de determinado erro
previsto, deve-se exibir mensagem para o usuário contendo o número do erro e
respectivo comentário que auxilie na elucidação do procedimento incorreto, como no
exemplo:

Select Case Err.Number


'Case statements for expected errors go here
Case 11 'Division by zero
MsgBox "Zero isn't a valid divisor", vbExclamation, Me.Caption
Case Else
Call UnexpectedError(Err.Number, Err.Description, _
Err.Source, Err.HelpFile, Err.HelpContext)
End Select
Resume ExitProcedure

Nesse exemplo a rotina UnexpectedError é uma função global (pública) que só é


chamada caso aconteça uma condição onde um erro de execução - não catalogado
previamente pelo programador - apareça, o que leva-nos a crer que há um bug no
procedimento. Este procedimento deve logar, em arquivo específico, essa ocorrência de
erro. O tratamento de erro deve, pelo menos, mostrar os detalhes como no exemplo a
seguir, porém, idealmente deveria fazer muito mais para registrar o erro:

Public Sub UnexpectedError(ByVal lngNumber As Long, _


ByVal strDescription As String, _
ByVal strSource As String, _
ByVal strHelpfile As String, _
ByVal lngHelpContext As Long)
On Error Resume Next
MsgBox "[" & strSource & "]" & vbCrLf & _
"Run-time error '" & CStr(lngNumber) & "':" _
& vbCrLf & vbCrLf & strDescription, vbExclamation, _
App.Title, strHelpfile, lngHelpContext

8
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Debug.Print "Case " & CStr(lngNumber) & " '" & strDescription
Debug.Assert False
End Sub

Na maioria das vezes a primeira linha executável de cada procedimento deve ser a linha
On Error GoTo ErrorHandler. A única exceção à regra é quando um procedimento
verifica os valores dos seus argumentos e propositadamente gera um erro de execução
(utilizando o comando Err.Raise) quando eles são inválidos. Neste caso, o código de
verificação vem antes do On Error GoTo linha. Por exemplo:

Public Sub Test(ByVal intValue As Integer)


If intValue < 1 Or intValue > 10 Then
Call Err.Raise(Number:=lngcInvalidValue, _
Description:=strcInvalidValue)
End If
On Error GoTo ErrorHandler
(...)

Sair de um Procedimento

Em geral, um procedimento só deve ter apenas um ponto de saída. Tendo apenas um


ponto de saída torna mais fácil ler o código e entender onde e quando ele termina sua
execução. Se você usar o código mencionado na Seção de Tratamento de Erros, perceba
que o ponto de saída será sempre o Exit Sub (para uma Sub), Exit Function (para
uma Function) ou Exit Property (para uma Property), geralmente localizado logo
acima da primeira linha de manipulação de erro(s). Outra maneira permitida para sair do
procedimento deve ser, apenas, através da utilização Err.Raise. A linha de código
contendo a instrução Err.Raise deve ser colocada antes da linha On Error GoTo, ao
validar os parâmetros passados para a rotina (ver a Seção de Tratamento de Erros) ou
no interior do trecho manipulador do erro.

Em alguns casos, pode haver a necessidade de se gerar propositadamente um erro


dentro do corpo do procedimento. Nesses casos, você deve definir explicitamente
qualquer variável de objeto ainda existente como Nothing (ver Nothing) para, depois,
sair do procedimento. Nesses casos, a saída do procedimento deve ser explicitamente
detalhada por um comentário que mostra a saída, consistindo de uma seta que se
estende desde o último caractere do texto do comentário até ao final da margem direita,
para fins de destaque do trecho de procedimento. O On Error GoTo 0 tem que ser
obrigatoriamente utilizado para desligar o tratamento de erros para este procedimento
antes de executar o comando Err.Raise. Por exemplo, se esse código aparece em
algum lugar após a linha do comando On Error GoTo, ele deve ser escrito como no
exemplo a seguir, para tornar explícito que há um Ponto de Saída no meio do
procedimento:

If intValue > 1000 Then


'Raise an Error---------------------------------------------->
Set rst = Nothing
On Error GoTo 0
Call Err.Raise(Number:=lngcInvalidValue, _
Description:=strcInvalidValue)
End If

9
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Laços For / Next e For Each / Next

O nome da variável índice utilizada no laço For / Next deve ser especificado na linha
do comando Next. Isto torna o código mais explícito e legível para fins de análise do laço
que está sendo executado. Por exemplo:

Dim iastrValue As Long


For iastrValue = LBound(astrValue) To UBound(astrValue)
MsgBox astrValue(iastrValue)
Next iastrValue

A variável de objeto usada para percorrer a coleção (array ou matriz coluna) deve ser
colocada na linha seguinte em um laço For Each / Next, como no exemplo:

Dim frm As Form


For Each frm In Forms
If Not (frm Is Me) Then
Unload Me
End If
Next frm

Declarações de desvio GoTo

Geralmente você consegue (e deve) evitar o uso de declarações GoTo num código VBA.
Utilize declarações GoTo apenas quando o código alternativo não ficar mais claro do que
como na instrução GoTo. Uma razão comum para usar uma instrução GoTo é a de
abandonar laços aninhados. Por exemplo:

For iastrOuterLoop = 1 To 10
For iastrInnerLoop = 1 To 100
'some other code
If astr(iastrOuterLoop, iastrInnerLoop) = "Done" Then
GoTo ExitNestedLoops
End If
'some other code
Next iastrInnerLoop
Next iastrOuterLoop
ExitNestedLoops:
'More code here

Cabeçalhos

Cada módulo deve começar com um código de cabeçalho que é algo parecido com isto:

'$Header: $
'*****************************************************************
Option Explicit
' Este módulo inclui definições de chamadas API do Windows

10
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

A linha de asteriscos é um apóstrofo seguido por 79 asteriscos. Veja a seção sobre


Linhas Longas. Cada procedimento Public deve começar com um bloco de cabeçalho
que é algo parecido com isto:

Public Sub Almanac(ByVal lngTrecena As Long, _


ByVal vein As veinc, _
ByVal lngRows As Long, _
ByRef alngBlack() As Long, _
ByRef alngRed() As Long, _
ByRef aveinRowStart() As veinc, _
ByRef aveinAlmanac() As veinc, _
ByRef lngComplete As Long)
'Generates a Maya almanac
'lngTrecena [in] Upper left corner trecena
'vein [in] Upper left corner veintena
'lngRows [in] Number of rows in the almanac
'alngBlack() [in] Black distance numbers across almanac
'alngRed() [out] Calculated Red trecena numbers across almanac
'aveinRowStart() [out] Calculated Leftmost shown veintenas in ‘
'almanac
'aveinAlmanac() [out] Actual veintenas implied by almanac
'lngComplete [out] Number almanac misses completing by.
'Return value: None
'If lngComplete returns zero then it is an almanac, if it is
non-'zero,
'then it misses completing and you'll need to report that. You
'will still
'need to handle the error encNotAnAlmanac because the black
'numbers
'in alngBlack must wrap back to the starting lngTrecena.
On Error GoTo ErrorHandler

Variáveis de Leitura e/ou gravação permitidas são sinalizadas com [in], [out], e
[inout]. Procedimentos de evento não precisam de um cabeçalho, a menos que o seu
escopo seja alterado para Public. Procedimentos privados (Private Sub ou Private
Function, por exemplo) podem precisar de um cabeçalho, dependendo do seu
contexto. Note que o nome da rotina não é repetido no texto dos comentários, tornando
possível mudar o nome do procedimento, sem a necessidade (ou obrigatoriedade...) de
alterar quaisquer comentários. Preferencialmente não inclua observações de histórico de
alterações ou de versões da rotina. Históricos e afins devem ser mantidos por sistemas
de controle de versão, e não, por programadores, uma vez que raramente são
devidamente atualizados. Os comentários são essencialmente dirigidos à pessoa que
chama o procedimento, e devem incluir apenas as informações suficientes para dizer à
pessoa como chamar o procedimento e usar os possíveis valores retornados. Após a
declaração do comando On Error GoTo, outros comentários podem, se necessário, ser
colocados para descrever algoritmos, bem como outros detalhes de implementações
feitas no código (mas não deixe de rever a seção Comentários).

Um programa freeware muito útil para tanto (e para tantas outras coisas) é o MzTools:
http://www.mztools.com/v3/mztools3.aspx

Indentação

11
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Indentações (Tab Stops) devem ser padronizadas em quatro espaços. Nenhum dos
membros de uma equipe de programação deve alterar esse padrão, pois tornará mais
difícil a edição de código por outros membros. Todo o código alojado dentro de um bloco
deve ser recuado em um nível de indentação a partir das linhas de limite desse bloco,
observadas algumas exceções contidas em outras partes destas Convenções. A
indentação facilita a visualização de início e fim do bloco. Um bloco é definido como o
trecho de código que esteja entre as seguintes palavras:

• Do/Loop
• Enum/End Enum
• For/Next
• For Each/Next
• Function/Exit Function/End Function
• If/Else/ElseIf/End If
• #If/#Else/#ElseIf/#End If
• Property/Exit Property/End Property
• Sub/Exit Sub/End Sub
• Type/End Type
• With/End With

Por exemplo:

For Each ci In tlio.Constants


Set nodChild = tvw.Nodes.Add( _
Relative:=nod.Key, _
Relationship:=tvwChild, _
Key:=ci.GUID & ci.Name, _
Text:=ci.Name, _
Image:=strcEnum)
nodChild.EnsureVisible
DoEvents
If mboolShowProperties Then
For Each mi In ci.Members
Set nodEnumChild = tvw.Nodes.Add(Relative:=nodChild.Key, _
Relationship:=tvwChild, _
Text:=mi.Name & _
strcEquals & _
mi.Value, _
Image:=strcConstant)
nodEnumChild.EnsureVisible
DoEvents
Next mi
End If
Next ci

Veja também a seção de blocos Select/End Select.

{Alternativa:. A equipe de programação pode definir um outro número como padrão para
espaços}
{Alternativa: Declarações Exit Function, Exit Property e Exit Sub podem ser
recuadas para o mesmo nível das linhas delimitadoras do bloco.}

Um programa freeware muito útil para criar/recriar a indentação no VBA é o


SmartIndenter, criado pelo Stephen Bullen (Office Automation Ltd):
http://www.oaltd.co.uk/DLCount/DLCount.asp?file=IndenterVBA.exe

12
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Instanciação

Uma variável de objeto não deve ser declarada com a cláusula New na mesma linha onde
é declarada, a menos que haja uma boa razão para fazê-lo. Ao contrário, essa
declaração deve ser dividida em duas linhas, como no exemplo:

Dim rst As ADODB.Recordset


Set rst = New ADODB.Recordset

E não, deste jeito:

Dim rst As New ADODB.Recordset

Dividi-la em duas linhas faz com que cada referência à variável rst seja executada um
pouco mais rápido do que se tivesse sido declarada na forma desaconselhada. Além
disso, a variável de objeto pode ser testada para ver se contém o valor Nothing.
Exemplo:

If rst Is Nothing Then


MsgBox "rst not initialized"
End If

Se o código fosse escrito em uma só linha, o código de verificação acima nunca iria exibir
a MsgBox, porque a referência à variável rst contida na instrução If faz com que o
objeto seja previamente instanciado antes que o operador Is consiga avaliá-la. Para
variáveis de objeto Public e Private, ocasionalmente há conveniência em se utilizar a
cláusula New, utilizando-se apenas uma linha para toda a declaração da variável, apesar
da aparente perda de benefício quanto ao desempenho.

Rótulos (Etiquetas ou Labels)

Rótulos no código devem ser justificados à esquerda, independentemente do nível de


indentação existente naquele trecho do código. devendo aparecer sozinhos em uma
linha. Por exemplo:

ExitProcedure:
On Error Resume Next

Linhas longas de código

A IDE do VBA acrescenta uma barra de rolagem horizontal, de forma a podermos


visualizar todo o conteúdo uma linha de código até o seu fim, rolando-a. No entanto, isto
dificulta a rápida visualização e leitura do código. Portanto, o comprimento de linhas deve
ser ajustado para que, preferencialmente, seja totalmente visto sem a necessidade de
rolagem de tela.

Uma linha física de código não deve exceder a 80 caracteres. Se uma linha lógica de
código excede 80 caracteres, deverá ser dividida em duas ou mais linhas físicas usando
o sublinhado de continuação: um espaço seguido de um caractere conhecido por
underline ou underscore (“_”). Todas as linhas físicas seguintes, interligadas pelo
13
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

underscore, devem ser indentadas (geralmente pelos quatro espaços), a contar da


primeira linha física. Colocar uma linha no topo do módulo com um apóstrofo seguido por
79 asteriscos por ser de valia para dimensionar adequadamente a janela de código do
VBE, de forma que o último asterisco esteja visível. A fonte Courier New deve ser
utilizada para exibir o código na janela de código. Deve-se escolher um local adequado
inserir a quebra de continuação da linha longa, com o objetivo de melhorar a legibilidade
do código restante. Ao quebrar as linhas que possuam uma lista separada por vírgulas,
deve-se quebrar a linha após cada uma das vírgulas, e antes do primeiro caractere de
texto (à exceção do espaço). Por exemplo:

Private Sub GetFiles(ByRef fso As Scripting.FileSystemObject, _


ByRef fld As Scripting.Folder)

Quando quebrar uma linha que é uma expressão construída por operadores, quebre a
linha logo antes de um operador de expressão. Por exemplo, quando a expressão é
construída com operadores de concatenação de strings, quebre-a assim:

strParameters = strParameters & strAdd _


& strPassingConvention & pmi.Name & strArray _
& strcAs & strDataType & strDefault

A linha do exemplo seguinte fica mais legível se quebrada desta forma:

strValue = "This is a very, very long string that will cause " _
& "the code to wrap. Because of this, you will need to break it."

Nesses casos quebre-a antes do início da próxima palavra. Note que o VBA realiza a
concatenação de Strings em tempo de execução, o que influencia (negativamente) no
desempenho. Em muitos casos, textos que são padrão (não se alteram durante a
execução do código) devem ser atribuídos a variáveis do tipo Const (Constante), ou
armazenados em um campo de banco de dados (MsAccess, por exemplo) ou de células
(no caso do MsExce)l, e recuperados a partir daí. Comentários não são continuados por
quebra de linha e, quando excederem os 80 caracteres, serão continuados mediante a
inserção, na nova linha, de um novo apóstrofo seguido do texto de continuação. Veja a
seção sobre Comentários. Não utilize excessivamente a quebra de linhas dentro de um
bloco de código. Quando isso ocorre é sinal de que deve-se criar uma sub-rotina
contendo a parte excessivamente indentada e, então, executa-la mediante chamada feita
pela rotina principal. Como padrão, faça isto sempre que o bloco de código principal
estiver constituído de mais do que oito níveis de indentação.

{Alternativa: coloque os operadores no final da linha antes de inserir o underscore, em


vez de coloca-los na próxima linha.}

Um programa freeware que acrescenta a rolagem vertical do código – o que não existe
na instalação padrão do MsOffice+WinXP – é o VBScroll, do Gazanov (obs.: não funciona
corretamente em dispositivos que utilizam TouchPad, como os Notebooks, gerando
conflitos): http://www.gasanov.net/Donation.asp?VBScroll.zip

Nothing

Deve-se explicitamente definir as variáveis de objeto como Nothing antes de permitir


que a variável seja destruída, por exemplo, com a finalização da rotina onde foi

14
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

dimensionada. Isto tem especial valor quando essa variável de objeto foi declarada com a
palavra reservada Dim. Por exemplo:

Public Sub Test(ByVal intValue As Integer)


'error handling omitted for clarity
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
'More code here
Set rst = Nothing
End Sub

Definir a variável de objeto como Nothing não é apenas uma boa prática de
programação. Se o objeto rst em questão possui alguma linha de código em seu evento
Class_Terminate, isto fará com que tal rotina seja executada, podendo possuir
comandos que vão modificar valores, atributos, etc. e, consequentemente, interferir no
resultado futuro do processamento . Além disso, deve-se definir uma variável de objeto
como Nothing sempre antes de sair do procedimento, caso esteja utilizando um
comando Err.Raise. Por exemplo:

Private Sub Test(ByVal intValue As Integer)


'error handling omitted for clarity
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
'More code here
If intValue > 1000 Then
'Raise an Error-------------------------------------------------->
Set rst = Nothing
On Error GoTo 0
Call Err.Raise(Number:=lngcInvalidValue, _
Description:=strcInvalidValue)
End If
Set rst = Nothing
End Sub

No exemplo mostrado, se a variável de objeto rst não for definida como Nothing antes
de ser executado o comando Err.Raise, o evento Class_Terminate do objeto rst
provavelmente irá alterar valores das propriedades do objeto Err de tal sorte que não
mais refletirá o correto número do erro armazenado na variável lngcInvalidValue. O
código contido no evento Class_Terminate será executado antes da execução do
trecho da rotina de erro, resultando num fluxo anormal na execução do código, o que
provoca um bug muito difícil de ser rastreado.

Passagem de parâmetros para uma rotina

Para todos os parâmetros passados a um procedimento deve-se especificar seu tipo de


dados de forma explícita, incluindo as variáveis do tipo Variant. Cada parâmetro deve
ser passado por valor utilizando a palavra reservada ByVal. Há algumas exceções,
quando serão, então, passados por referência (ByRef), como nas seguintes situações:

• O VBA não permite que certos tipos de sejam passados por valor, tais como matrizes,
tipos definidos pelo usuário (UDT), e variáveis de objeto.

15
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

• Você deseja que o valor alterado desse parâmetro seja retornado para a rotina
chamadora.
• O parâmetro para passagem ao procedimento de evento é especificado como sendo
ByRef quando o VBA os criou.
• Os argumentos para uma declaração Declare de uma API do Windows devam coincidir
com a definição padronizada para a correta chamada da função da DLL.

Mesmo nos casos em que o argumento deva obrigatoriamente ser passado por
referência a outro procedimento, deve-se sempre prefixar o parâmetro com a cláusula
ByRef, embora este já seja o padrão de consideração do VBA. Isto torna mais explícito
que você quis passar esse parâmetro por referência. Quando duplo-clicamos em
determinado item da lista de eventos de um objeto (por exemplo, um evento MouseMove
de um Userform) o VBA insere, automaticamente, o código padrão de início e fim desse
procedimento de evento na janela de código. Mesmo aí devemos alterar o que foi escrito
- de forma automática pelo VBA – fazendo a adição da(s) cláusula(s) ByVal e/ou ByRef,
se necessário (diferentemente do comportamento do VB6, o VBA já as adiciona) devendo
também alterar os nomes de parâmetro sugeridos automaticamente, aplicando as regras
de Convenções de Nomenclatura adequadas. Por exemplo, o VBA insere estas linhas de
código ao procedimento de evento MouseMove de um Userform (a linha inicial aqui foi
quebrada em duas para perfeito entendimento):

Private Sub UserForm_MouseMove(ByVal Button As Integer, _


ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

End Sub

Isso deve ser alterado para ser lido/escrito da seguinte forma:

Private Sub UserForm _MouseMove(ByVal intButton As Integer, _


ByVal intShift As Integer, ByVal sngX As Single, ByVal sngY As
Single)

End Sub

Utilizando as regras das Convenções de Nomenclatura, ao indicar o tipo de dados e as


palavras reservadas, você facilita a análise de apreciação das mudanças que podem
ocorrer nos parâmetros.

Uso de Parênteses

Sempre utilize parênteses onde a leitura da linha pode não estar clara. Por exemplo,
suponha uma linha de código escrita assim:

If Not frmTest Is Nothing Then

Pode não ficar claro que o operador Is tem maior precedência que o operador Not nesta
linha. Altere a linha, utilizando parênteses, assim:

If Not (frmTest Is Nothing) Then

Isso deixa claro em que ordem de precedência os operadores são avaliados. A regra
geral é a de que, se houver qualquer dúvida quanto a precedência de operador, utilize
parênteses para deixar isto claro.
16
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Escopo do procedimento

Sempre utilize a declaração de escopo privado (Private) em um procedimento, a


menos que você precise expor esse procedimento para além do módulo corrente. Em
uma biblioteca, use o escopo Friend quando você precisa de um escopo mais
abrangente. Use o escopo Public somente quando o acesso a esse procedimento, por
códigos localizados em outras áreas do aplicativo, precise ser feito. Por exemplo:

Private Sub Test()

Gerando erros de forma propositada

Quando você propositadamente gera um erro de execução dentro de uma rotina, a ser
tratado pelo código que a chamou, o número de erro que você provoca deve ter um
número de erro exclusivo. Para este fim, o VBA define uma constante denominada
vbObjectError que garante que os erros assim criados não gerarão conflitos com
aqueles predefinidos no VBA. No entanto, todas as bibliotecas que podem gerar esse tipo
de erro devem gera-los numa faixa maior do que a convencionada para a faixa de
vbObjectError. Assim, deve-se cuidar para diferenciar a faixa de numeração dos erros
customizados para uma dada biblioteca de código, daqueles gerados por outra biblioteca.
Não há nenhuma maneira de garantir a completa diferenciação, porém, as chances
podem ser otimizadas, iniciando uma faixa de números de erros de forma aleatória, no
intervalo de 512 a 32767 a mais do que a de vbObjectError. Como exemplo, nenhuma
biblioteca que uma Organização produza jamais deverá possuir números de erro em
conflito com outra biblioteca dessa mesma Organização. Por exemplo: A empresa XYZ
Soft pode iniciar a numeração de seus erros na faixa vbObjectError + 4096. Portanto,
a primeira biblioteca produzida pela XYZ Soft gerará erros na faixa de vbObjectError +
4096 e até vbObjectError + 4146 e, seguindo esse raciocínio, a segunda biblioteca o
fará a partir de vbObjectError + 4147 e até vbObjectError + 4196, etc

Bloco Select / End Select

O bloco Select / End Select é indentado diferentemente de outros blocos (ver


Indentação). Os blocos Case dentro do bloco Select / End Select ficam alinhados
com as palavras reservadas Select / End Select. O código dentro de um bloco
Case é indentado em uma tabulação a partir da instrução Case, como no exemplo:

Select Case Err.Number


Case tliErrCantLoadLibrary
Err.Raise Number:=Err.Number, _
Description:=Err.Description, _
Source:=Err.Source
Case 35602 'This key is already associated with an element of
'this collection
Set nodChild = tvw.Nodes.Item(cci.GUID)
nodChild.Image = "InstClass"
Resume NextItem

17
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Case Else
Call UnexpectedError(Err.Number, _
Err.Description, _
Err.Source, _
Err.HelpFile, Err.HelpContext)
End Select

Em outros padrões de codificação, é comum indentar blocos Case com um nível de


tabulação desde essa instrução, a partir da linha inicial do Select / End Select. No
entanto, isso faz com que o código a ser indentado fique com duas paradas de tabulação,
uma logo após a outra. Na forma aqui proposta, a legibilidade do código é muito boa, se
não melhor, porém é preciso algum tempo para se acostumar a identificar o primeiro
bloco Case, pois estará indentado no mesmo nível da primeira linha do bloco (a
expressão Select Case). Veja também a nota sobre blocos Case Else na seção
sobre Asserções.

{Alternativa: Indentação dos blocos Case em um nível, relativamente ao início e fim do


trecho do bloco (as linhas Select / End Select). Em seguida, indentar o código
contido entre os blocos Case em mais um nível, pelo menos.}

O freeware SmartIndenter, comentado anteriormente, faz essa indentação, e oferece


opções de customização para o bloco Select Case)

Conversão de tipo de dados

O VBA é considerado uma linguagem pouco padronizada. Você pode construir


expressões como esta:

strValue = "Seu pedido total custa $" & intQuantidade * curPreço.

As “Convenções de Nomenclatura RVBA”, de Greg Reddick, disponíveis no site


http://www.xoc.net/standards/rvbanc.asp, auxiliam na identificação de possíveis erros. Se
nos depararmos com uma linha de código, tal como esta, poderemos estar diante de um
bug em potencial:

intValue = lngInput

Se o valor da variável lngInput é ou passa a ser, por exemplo, 90.000, esta linha irá
causar um erro de execução (estouro – Overflow). O fato de que os tipos de dados das
duas variáveis são diferentes é um sinal claro de que poderemos ter problemas. Se,
contudo, tivermos a certeza de que o valor em lngInput só poderá estar na faixa de 1 a
1000, por exemplo, pode ser razoável fazer uma atribuição como esta:

Debug.Assert lngValue >= 1 And Debug.Assert lngValue <= 1000


intValue = CLng(lngInput)

Veja também a seção sobre Asserções.

Declaração de variável

18
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Cada variável deve ser declarada explicitamente, incluindo aí as variáveis do tipo Variant,
que são o padrão utilizado pelo VBA, quando não especificado um tipo para uma
determinada variável. Usando a palavra reservada Option Explicit como primeira
linha do módulo fará com que o VBA imponha essa condição ao programador, durante a
criação do código. O diálogo “Opções”, obtido a partir do menu Ferramentas > Opções
exibe uma série de itens configuráveis, dentre os quais, a possibilidade de inserção
automática dessa palavra reservada tão logo de adicione um módulo em branco. A
imagem mostra as diversas possibilidades de configuração da IDE. Explore-as.

Aba “Editor”, com a configuração ideal:

Aba “Geral”, com a configuração ideal:

19
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

A grade com 4 X 4 nos oferece maior precisão para aposição dos objetos por sobre o
formulário. A opção de “Notificar antes de perda de estado” assegura que receberemos a
informação de interrupção do código imediatamente antes que aconteça, embora não
possa ser evitado, caso o confirmemos. Desmarcar os itens de “Compilar” faz com que,
sempre que alterado, todo o código seja novamente compilado, assegurando que esteja
“sempre correto”.

Uma variável do tipo Variant, deve ser declarada como:

Dim varValue As Variant

Ao invés de deixá-la implicitamente definida (padrão do VBA):

Dim varValue

Cada variável deve ser declarada em uma linha. Isto impede que seja criado este bug:

Dim intValue, intTest As Integer

Esta linha de declaração mostra a intenção do programador de atribuir o tipo Integer às


duas variáveis, mas a primeira variável é definida como uma Variant. Se, ao invés
disso, as declarações fossem feitas cada uma em uma linha, esse problema não existiria:

Dim intValue As Integer


Dim intTest As Integer

Além disso, ao declarar cada variável em uma linha, você pode usar o atalho do teclado
Ctrl + Y para recortar-e-mover (eliminando o recorte do corpo de código e carregando-o
na Área de Transferência do Windows), independentemente de onde esteja o cursor –
desde que esteja em algum ponto dessa linha - para, em seguida, colá-lo em outro local
no código. No caso do exemplo bugado, como existem múltiplas declarações na mesma
linha, a edição não será tão fácil.

Inicialização de variáveis

Algumas linguagens de programação permitem a declaração da variável e, na mesma


linha, a atribuição de um valor inicial. O VBA não permite isso. No momento de sua
declaração, a variável assume um valor padrão, com base no seu tipo de dados. No
entanto, uma variação de sintaxe pode ser usada dentro de um procedimento para
enfatizar, no código, o seu valor padrão atribuído. O VBA permite que você coloque
múltiplas linhas lógicas de código na mesma linha física se você separá-la com dois
pontos (:). Também é permitido que você coloque, numa só linha, tanto a declaração de
variáveis, bem como, linhas de código executável. Portanto, dentro de um procedimento,
você pode inicializar uma variável e dar-lhe um valor padrão em uma única linha física,
como neste exemplo:

Public Sub Test()


'Error handling omitted for clarity
Dim intValue As Integer: intValue = 7
Dim strTest As String: strTest = "Default Value"
'other code
End Sub

20
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Variáveis: Escopo e Existência

Variáveis sempre devem ser declaradas com o menor nível de escopo, e com tempo de
vida o mais curto possível. Assim deve-se, preferencialmente, declarar variáveis com Dim
dentro do procedimento, e não, na seção de Declarações Gerais do módulo. Se for
necessário maior escopo para essa variável, pode usar a cláusula Static. Se, ainda
assim chegar-se à conclusão de que tal escopo ainda é insuficiente deve-se utilizar a
cláusula Public(*). Só utilize o escopo Public como último recurso. Variáveis Public
declaradas em um módulo padrão são globais e podem ser (inadvertidamente?) alteradas
por qualquer outro trecho de código, em todo o projeto. Isso faz com que a depuração do
código, por meio de observação das mudanças de seu valor se torne algo mais difícil
para se chegar a uma boa conclusão. Variáveis Public devem ser utilizadas apenas
com contexto de sua inicialização. Nenhuma outra parte do código deve alterá-las para
recupera-las em outro trecho do programa, conferindo-as uma característica estática no
restante da execução do programa. Nos casos em que isto se torne necessário, devem-
se utilizar parâmetros de procedimentos ou propriedades de formulários ou de objetos
para passar essa informação. Como regra geral, se houver mais de 20 variáveis Public
no programa, é um sinal de alerta de que a concepção do programa está errada. Criar
constantes Public (Public Const ctestrFixed as string = “1.0”, por
exemplo) é permitido e encorajado. Veja a seção sobre Constantes.

(*) – No texto original, foi colocada a palavra “Private”, o que é incorreto.

21
Regras Reddick VBA (RVBA) - Convenções de Codificação – v. 0.90

Sobre o autor do texto original:

Greg Reddick é o presidente da Xoc Software, uma empresa de desenvolvimento de


software que atua no desenvolvimento de programas para o Visual Basic, Microsoft
Access, C/C++, e para a web. Programa profissionalmente há mais de 20 anos, incluindo
quatro anos trabalhando na equipe de desenvolvimento do Microsoft Access. Ele é co-
autor de três livros sobre Access, desenvolveu estas e as Convenções de Nomenclatura
Reddick VBA (RVBA) ampla e internacionalmente utilizadas, e promove seminários
públicos e empresariais com foco em Visual Basic.NET, C #, ASP.NET e XML / XSLT.
Greg pode ser contactado pelo endereço eletrônico grr@xoc.net ou pelo site Xoc
Software, http://www.xoc.net.

O documento original foi:


- traduzido para Pt-Br,
- acrescido com informações de texto e de imagens;
- adaptado especificamente para o VBA (foi excluído o que se aplica apenas ao VB6), e
- corrigido por ALeXceL@Gmail.Com,
para leitura pelos associados da Lista de Discussão ExcelBr, podendo ser livremente
distribuído, sem quaisquer ônus, desde que mantidas as fontes de informação,
notadamente, a da documentação original em inglês, tendo em vista que o seu autor
(Greg Reddick) autoriza sua livre distribuição. Este documento não substitui o original,
disponível no website da Xoc Software: http://www.xoc.net.

22

Das könnte Ihnen auch gefallen