Ir para o conteúdo

ByVal, ByRef, Optional e ParamArray

Introdução

Por cada vez que criamos um método, é nos impingido o ByVal antes da nossa variável de argumento. O que são afinal estas designações e por que devemos saber usá-las? Estas "keywords" são na verdade muito importantes no que toca ao método como os nossos argumentos são passados.

ByVal

O ByVal é a "keyword" que é automáticamente inserida atrás das nossas variáveis de argumentos. Exprimentemos o seguinte bloco de código:

Private Sub FazerAlgo(Valor1 As String, Valor2 As String)
End Sub

Ao passarmos para a próxima linha, ou clicarmos em qualquer lugar no editor, reparamos que as palavras ByVal são automaticamente inseridas atrás das variáveis Valor1 e Valor2. Passamos a ter:

Private Sub FazerAlgo(ByVal Valor1 As String, ByVal Valor2 As String)
End Sub

ByVal provoca a passagem de um valor. Qualquer valor ou variável que seja inserida em argumento é passada para o método e qualquer alteração a este valor, dentro do método, não se reflecte em mais lado nenhum senão no próprio sub. Por exemplo:

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Palavra1 As String = "Olá"
        Dim Palavra2 As String = "Mundo"
        FazerAlgo(Palavra1, Palavra2)
        MessageBox.Show(Palavra1 & " " & Palavra2)
    End Sub

    Private Sub FazerAlgo(ByVal Valor1 As String, ByVal Valor2 As String)
        Valor1 = "Adeus"
        MessageBox.Show(Valor1 & " " & Valor2)
    End Sub

End Class

Os valores das variáveis Palavra1 e Palavra2 são passados para o método FazerAlgo. Ainda que a variável Valor1 seja alterada para Adeus, como se verifica na primeira MessageBox, a segunda MessageBox continua a apresentar-nos Olá.

Nota: Primeira e segunda messagebox refere-se ao fluxo e não à ordem linear.

ByRef

ByVal provoca a referência de um valor. É importante perceber a diferença entre oferecer um valor e oferecer uma referência. Qualquer alteração a um valor, não se reflecte na origem do dado, mas uma alteração a uma referência altera a origem do dado, uma vez que se trata de uma referência ao dado original. Senão vejamos:

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Palavra1 As String = "Olá"
        Dim Palavra2 As String = "Mundo"
        FazerAlgo(Palavra1, Palavra2)
        MessageBox.Show(Palavra1 & " " & Palavra2)
    End Sub

    Private Sub FazerAlgo(ByRef Valor1 As String, ByRef Valor2 As String)
        Valor1 = "Adeus"
        MessageBox.Show(Valor1 & " " & Valor2)
    End Sub

End Class

Este bloco é práticamente idêntico ao anterior, excepto a "keyword" atrás das variáveis. Com ByRef, verificará agora que ambas as MessageBox vão informar Adeus Mundo, pois a variável Valor1, no método, foi alterada, causando alteração na sua referência original, a Palavra1 do Form_Load. Isto é muito importante, pois estando ciente desta diferença é possível, por exemplo, uma função que devolva uma série de valores. Por exemplo:

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim ResultadoDivisao As Double
        If Dividir(10, 2, ResultadoDivisao) = False Then MsgBox("Não podes dividir por zero!") : Exit Sub
        MessageBox.Show(ResultadoDivisao)
        If Dividir(10, 0, ResultadoDivisao) = False Then MsgBox("Não podes dividir por zero!") : Exit Sub
        MessageBox.Show(ResultadoDivisao)
    End Sub

    Private Function Dividir(ByVal Parcela1 As Double, ByVal Parcela2 As Double, ByRef Resultado As Double) As Boolean
        If Parcela1 = 0 Or Parcela2 = 0 Then Return False
        Resultado = Parcela1 / Parcela2
        Return True
    End Function

End Class

No exemplo, enviamos o valor das parcelas e uma referência à variável ResultadoDivisao. Ao chamar o método, são nos devolvidos dois valores: o ResultadoDivisao, por referência, e o Boolean a informar o sucesso. Ao correr o exemplo, verificamos que a primeira MessageBox informa o resultado da divisão, passado por referência, e a segunda MessageBox informa que uma das parcelas é zero, porque o boolean de retorno volta a zero (False). Desta forma, conseguimos um método que devolve dois valores: uma variável do tipo Double e outra do tipo Boolean.

Optional

Em adição ao ByVal e ByRef, ainda podemos determinar o argumento como Optional, ou opcional. Ao marcarmos um argumento como opcional, ao chamar o método podemos optar por não enviar um valor nesse argumento, o que pode ser usado para determinadas finalidades. Como o argumento existe, e tem de transportar um valor, ao definirmos o argumento temos obrigatóriamente que lhe atribuír um valor para ser usado como o valor padrão. Ainda que optemos por não passar nenhum valor, é o valor padrão que lhe está atribuído. Por exemplo:

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        MessageBox.Show(Unir("Olá ", "mundo! ", ))
        MessageBox.Show(Unir("Olá ", , "Sou eu de novo!"))
        MessageBox.Show(Unir("Olá ", "mundo! ", "Sou eu de novo!"))
    End Sub

    Private Function Unir(ByVal Palavra1 As String, Optional ByVal Palavra2 As String = "", Optional ByVal Palavra3 As String = "") As String
        Return Palavra1 & Palavra2 & Palavra3
    End Function

End Class

Os argumentos Palavra2 e Palavra3 são opcionais. Podemos optar por enviar valores ou não. O método está preparado para não receber valores nos argumentos da chamada. Como o valor padrão, é neste caso nada, ou "", ao não enviarmos o argumento estamos a ignorá-lo na união.

Importante: Os argumentos opcionais têm obrigatóriamente que ser os últimos argumentos do método.

Importante: Nem todos os tipos de dados podem ser passados por argumentos opcionais. Tipos de dados compostos são um exemplo de um tipo de dados não aceite.

ParamArray

Para além do ByVal, ByRef e Optional, podemos também definir um ParamArray que é uma matriz de elementos de um determinado tipo. À semelhança do Optional, o ParamArray só pode ser o último dos argumentos.

Exemplo:

Public Class Form1

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        MessageBox.Show(Unir("Olá, ", "está ", "tudo ", "bem?"))
    End Sub

    Private Function Unir(ParamArray Palavras() As String) As String
        Dim Resultado As String = String.Empty
        For Each S As String In Palavras
            Resultado &= S
        Next
        Return Resultado
    End Function

End Class