What is behind this difference in parentheses effect in VBA?
I've not seen this in other languages but I see it a lot in VBA (which I just started working with). Suppose you have a table in Word and wish to set the rows to a certain height. If you do this
tbl.Rows.SetHeight InchesToPoints(1), wdRowHeightExactly
the table's rows indeed are set to 72 points or 1 inch in height. However, if you surround the arguments in parentheses, something I did instinctively, VBA gives an error -- expected:=
.
I can solve this by using a throw-away variable, like this
x = tbl.Rows.SetHeight (InchesToPoints(1), wdRowHeightExactly)
or, of course, I can simply not surround the arguments in parentheses.
Microsoft's documentation on the SetHeight method doesn't mention any return value, but in any case, this behavior is extensive throughout VBA. It's not specific to the SetHeight method.
My questions: What is this called? Should I use a throw-away variable or throw away the parentheses? What's the logic from Microsoft's point of view? Are there consequences to using one or the other, consequences I can't imagine (because they are unknown unknowns)?
Solution 1:
Definitely don't introduce a "throw-away variable", especially if it's not declared, and especially if what you're invoking is a Sub
, a procedure that doesn't return any value. Well you can, if you don't mind a compile-time error:
Expected Function or variable.
Now...
this behavior is extensive throughout VBA. It's not specific to the SetHeight method.
@Yoe3k put it nicely:
As for what it is called, I would guess "correct syntax" is the most appropriate word.
That's the whole answer: it's not about SetHeight
, it's about how VBA's implicit procedure/member call syntax works. The explicit Call
syntax has been obsolete since the wonderful advent of implicit calls, about a quarter of a century ago. So splattering Call
keywords left & right and all over your code will, indeed, keep you the parentheses... if you hold them so dear.
But the "logic" of the implicit call syntax isn't all that complicated, really.
What follows is what I wrote on Documentation.SO about VBA and parentheses, hope it helps.
This is confusing. Why not just always use parentheses?
Parentheses are used to enclose the arguments of function calls. Using them for procedure calls can cause unexpected problems.
Because they can introduce bugs, both at run-time by passing a possibly unintended value to the procedure, and at compile-time by simply being invalid syntax.
Run-time
Redundant parentheses can introduce bugs. Given a procedure that takes an object reference as a parameter...
Sub DoSomething(ByRef target As Range)
End Sub
...and called with parentheses:
DoSomething (Application.ActiveCell) 'raises an error at runtime
This will raise an "Object Required" runtime error #424. Other errors are possible in other circumstances: here the Application.ActiveCell
Range object reference is being evaluated and passed by value regardless of the procedure's signature specifying that target would be passed ByRef
. The actual value passed ByVal
to DoSomething
in the above snippet, is Application.ActiveCell.Value
.
Parentheses force VBA to evaluate the value of the bracketed expression, and pass the result ByVal
to the called procedure. When the type of the evaluated result mismatches the procedure's expected type and cannot be implicitly converted, a runtime error is raised.
Compile-time
This code will fail to compile:
MsgBox ("Invalid Code!", vbCritical)
Because the expression ("Invalid Code!", vbCritical) cannot be evaluated to a value.
This would compile and work:
MsgBox ("Invalid Code!"), (vbCritical)
But would definitely look silly. Avoid redundant parentheses.
Solution 2:
The most serious consequence of using parentheses inappropriately can best be demonstrated by code such as:
Sub Test()
Dim r As Range
Set r = Range("A1")
TestSub r
TestSub (r)
End Sub
Sub TestSub(parm As Range)
MsgBox parm.Address
End Sub
In that code TestSub r
correctly passes a range object to TestSub
. However, placing parentheses around the r
, i.e. TestSub (r)
causes VBA to evaluate r
using its Value
property and is therefore equivalent to TestSub r.Value
. This then gives an error as it is passing a Variant
(maybe a Variant/Double
with a value of 123.45
) to a subroutine that is expecting a Range
.
It is basically just incorrect syntax to enclose parameters to a Subroutine within parentheses. They should only be used when a Function is returning a value.
P.S. I apologise that my example is Excel VBA. I hadn't noticed that the question was Word VBA, plus I could knock up an Excel example quicker than I could research Word VBA enough to give an example in it. The principle is the same in both though.