VBA - Artigo 018 - Procedimentos: passando argumentos por valor e por referência

Progredindo em VBA no Microsoft Excel

Procedimentos: passando argumentos por valor e por referência

Uma dúvida muito comum entre usuários do VBA é com relação a alguns termos encontrados em alguns eventos com parâmetros: ByRef e ByVal. O que significam isso e qual o seu resultado? ByRef e ByVal indicam se o parâmetro será passado por referência ou por valor, respectivamente.

Explicando melhor: se o parâmetro for passado com ByRef, o VBA usará a referência original da variável no processamento, ou seja, se o valor for alterado na sub-rotina ou função, a variável original passada como argumento também terá seu valor alterado. Caso o parâmetro for passado com ByVal, a sub-rotina ou função receberá uma cópia do valor e quaisquer alterações não terão impacto na variável original.

Para entender isso de uma maneira mais prática, vejamos um código simples de exemplo:

Sub Teste()

    Dim X As Integer

    X = 10
    Debug.Print "Valor de X = " & X
    Debug.Print "Resultado de Dobro = " & Dobro(X)
    Debug.Print "Valor de X = " & X

End Sub

Function Dobro(ByRef Valor As Integer) As Integer

    Valor = Valor * 2
    Dobro = Valor

End Function

A sub-rotina Teste associa um valor à variável X, mostra seu resultado, mostra o resultado da função Dobro e depois mostra o resultado de X novamente após a função Dobro ter sido executada. Note que o parâmetro Valor na função Dobro foi passada como referência (ByRef), ou seja, qualquer alteração feita em Valor irá refletir na variável original. Executando o código temos o seguinte resultado na área de Verificação imediata:

Valor de X = 10
Resultado de Dobro = 20
Valor de X = 20

Como podemos ver, a variável X mudou de valor após ter sido referenciada na função Dobro. Altere a função para ByVal conforme abaixo:

Function Dobro(ByVal Valor As Integer) As Integer

Agora execute novamente a sub-rotina Teste e veja o resultado:

Valor de X = 10
Resultado de Dobro = 20
Valor de X = 10

Agora o valor de X não sofreu alterações e se manteve intacto, pois foi usada uma cópia do valor de X, não a referência original da variável.

E se não for informado ByRef ou ByVal, qual é a forma padrão que o Excel utiliza? Simples de descobrir: retire da função Dobro, deixando sem especificação se é ByRef ou ByVal e veja o resultado:

Valor de X = 10
Resultado de Dobro = 20
Valor de X = 20

O padrão no VBA é ByRef, ou seja, utilizará a variável original passada como argumento. Isto não é um problema se você não edita o valor do parâmetro na função ou na sub-rotina, mas pode causar problemas se você altera o valor desse parâmetro.

E como funciona com objetos? Será que é do mesmo jeito? Antes de conferir isso, precisamos de entender que objetos são tratados de forma diferente. Por exemplo, quando vamos associar uma célula a uma variável do tipo Range precisamos usar Set antes da associação, como abaixo:

Set Celula = Range("A1")

Da mesma forma, uma sub-rotina ou função que usa um objeto como parâmetro é escrita de forma distinta. Há três maneiras de fazer a chamada: usando Call, omitindo os parênteses ou ainda especificando os parâmetros. Vejamos as três formas abaixo:

Call Dobro(Celula)
Dobro Celula
Dobro Intervalo:=Celula

As três formas funcionam da mesma maneira, utilize aquela que achar melhor.

Agora vejamos o código para ver como funciona ByRef e ByVal com objetos:

Sub Teste()

    Dim Celula As Range

    Set Celula = Range("A1")

    Debug.Print "Valor da célula A1 = " & Celula.Value
    Dobro Celula
    Debug.Print "Valor da célula A1 = " & Celula.Value

End Sub

Function Dobro(ByRef Intervalo As Range) As Range

    Debug.Print "Valor do intervalo recebido = " & Intervalo.Value
    Intervalo.Value = Intervalo.Value * 2
    Debug.Print "Valor do intervalo após o processamento = " & Intervalo.Value

End Function

Coloque na célula A1 o valor 10 e efetue o teste. A saída será a seguinte:

Valor da célula A1 = 10
Valor do intervalo recebido = 10
Valor do intervalo após o processamento = 20
Valor da célula A1 = 20

O valor da célula A1 passou a ser 20. Como vimos, ByRef passa a referência e por isso tudo funcionou como esperado. Altere a função para tratar o parâmetro por valor, substituído ByRef por ByVal, coloque o valor 10 novamente na célula A1 e teste. Vejamos o resultado:

Valor da célula A1 = 10
Valor do intervalo recebido = 10
Valor do intervalo após o processamento = 20
Valor da célula A1 = 20

O resultado foi exatamente o mesmo. Por que isso acontece? No VBA (e em outras linguagens de programação) os objetos são sempre passados como referência, ou seja, qualquer alteração feita irá refletir no objeto usado como parâmetro. Portanto, não importa se o objeto está acompanhado de ByVal, o resultado será como ByRef.

Particularmente não costumo alterar o valor das variáveis usadas como parâmetro, por isso nem uso ByRef ou ByVal no VBA. Somente nos objetos é que costumo alterar e o efeito desejado é justamente alterar o objeto passado como parâmetro. O importante é sempre testar bem a sub-rotina ou função para saber se está tudo funcionando bem e está com o efeito desejado.

Quaisquer dúvidas, comentários ou sugestões, use a seção de comentários abaixo. Até o próximo artigo.

Pedro Martins


Formado em Tecnologia em Eletrônica Digital, já trabalhou como artefinalista, eletrotécnico, programador de CLP (para máquinas industriais) e analista de sistemas em sistema bancário, programando em COBOL.
Mexe com computadores e programação desde a segunda metade dos anos 1980, quando teve um MSX e aprendeu a programar em BASIC. É a favor da disseminação do conhecimento.




Catálogo de aulas (NOVIDADE)

Criei um catálogo de aulas para ajudar você em seus estudos. Acesse clicando na imagem abaixo ou clique aqui.


Postagem Anterior Próxima Postagem