VBA: Usage of parentheses for a method

The “official help page” is on GitHub, it’s actively maintained, and multiple changes are being merged every day. If there’s an error in an example, open an issue for it, or better, submit a fix yourself!

The example is wrong, the parentheses either shouldn’t be there, or the expression should be on the right-hand side of a Set assignment to some object variable.

you have to use parentheses when assigning the return value of a method (or function) to a variable

Correct.

When you don’t capture the return value, you don’t put the parentheses. If you do, the VBE gives you a hint. If you copied the example from the docs, it would look like this in the editor:

ActiveWorkbook.Sheets.Add (Before:=Worksheets(Worksheets.Count))

Note the space. If you captured the return value:

Set newSheet = ActiveWorkbook.Sheets.Add(Before:=Worksheets(Worksheets.Count))

No space.

Just to make sure I get the VBA logic: #3 gives me an error because the parentheses to VBA means the value (= the new worksheet) gets returned, but there’s no variable to assign it to? And #6 is the opposite case?

There’s more to it than that. Consider a simpler example:

MsgBox "hi", vbOkCancel

If we wanted to capture or otherwise use the return value, we would need the parentheses:

If MsgBox("hi", vbOkCancel) = vbOk Then

If we added parentheses without capturing/using the return value, we would have this:

MsgBox ("hi", vbOkCancel)

So what does this space mean?

To the VBA compiler, it means “this isn’t the argument list, it’s the first argument, and this is a value expression: evaluate it, and send the result ByVal to the invoked procedure“. The problem, of course, is that ("hi", vbOkCancel) isn’t an expression, and can’t be evaluated, and we have a compile error.

So back to the docs example: Before:=Worksheets(Worksheets.Count) isn’t a legal expression either – it’s an argument list consisting of one named argument… but syntactically it’s not the argument list: parenthesized, it’s an expression that, if it could be evaluated, would be passed to the first argument of the parameter list, ByVal – like so:

ActiveWorkbook.Sheets.Add Argument1:=(the result of the expression)

The ByVal nature of the parenthesized argument is basically an accident: when VBA evaluates the expression, it gets a value… but that value is up in the air, there’s no local reference to it – so even though the invoked procedure is accepting a ByRef argument, since the caller isn’t holding a reference to that argument, it’s discarded – effectively producing the exacty same result as if the function took the parameter ByVal.

Confusing? This should help:

Public Sub Test()
    Dim foo As Long

    DoSomething (foo) ' evaluates the expression, passes the result of that expression
    Debug.Print foo ' prints 0

    DoSomething foo ' passes a reference to the local variable
    Debug.Print foo ' prints 42
End Sub

Private Sub DoSomething(ByRef value As Long)
    value = 42
End Sub

Leave a Comment