Jérôme
Jérôme

Reputation: 27027

ByRef and ByVal in VBScript

I'm facing something weird in VBScript. When writing a procedure where I want the parameter to be passed by reference, the way of calling this procedure changes the way the parameter is passed !

Here is an example :

Sub IncrementByRef(ByRef Value)
   Value = Value + 1
End Sub

Sub IncrementByVal(ByVal Value)
   Value = Value + 1
End Sub

Dim Num
Num = 10
WScript.Echo "Num : " & Num
IncrementByRef(Num) : WScript.Echo "IncrementByRef(Num) : " & Num
IncrementByRef Num  : WScript.Echo "IncrementByRef Num : " & Num
IncrementByVal(Num) : WScript.Echo "IncrementByVal(Num) : " & Num
IncrementByVal Num  : WScript.Echo "IncrementByVal Num : " & Num

And here is the output :

U:\>cscript //nologo byrefbyval.vbs
Num : 10
IncrementByRef(Num) : 10
IncrementByRef Num : 11
IncrementByVal(Num) : 11
IncrementByVal Num : 11

U:\>

When specifying the the parameters are passed ByVal, it works as expected, no matter the way the procedure is called. But when specifying the parameters are passed ByRef, it will work as expected if calling the procedure this way :

IncrementByRef Num

but not this way :

IncrementByRef(Num)

This seems weird to me. Is there a way to make sure the parameters are passed ByRef, no matter how the procedure is called ?

Upvotes: 34

Views: 48282

Answers (6)

Joel Coehoorn
Joel Coehoorn

Reputation: 416049

It's a feature, not a bug:
http://msdn.microsoft.com/en-us/library/ee478101.aspx

A ByRef parameter is passed by value if the argument is enclosed in parentheses and the parentheses do not apply to the argument list.

The parentheses apply to the argument list if one of the following is true:

  • The statement is a function call that has an assignment to the returned value.

  • The statement uses the Call keyword. (The Call keyword can optionally be used for a subroutine call, or for a function call without an assignment.)

Therefore if you call a Function (not a Sub) with one argument, the argument is ByRef, you do not use the Call keyword, and you don't assign the returned value to anything, the parentheses tell the compiler to pass the argument ByVal instead.

From there we learn two things:

  1. The Call keyword is only mostly useless, and adding it here can resolve your issue.
  2. We have yet another reason to avoid ByRef. This was more common in the VB6/VBScript era. In the .Net era, ByVal gives similar performance to ByRef and should be used much more often anyway.

Upvotes: 19

Helen
Helen

Reputation: 97847

Eric Lippert has a great article on using parentheses in VBScript: What do you mean "cannot use parentheses?" Your example illustrates one of the points he mentions, namely: enclosing a ByRef argument in parentheses passes it as ByVal.

In short, parentheses in VBScript subroutine calls can be put not only around the argument list, but also around individual arguments (in which case they are forced ByVal). And VBScript only expects the argument list be enclosed in parentheses if the Call keyword is used. Since the IncrementByRef(Num) call doesn't use the Call keyword, VBScript treats parentheses as applied to the subroutine argument and thus passes it ByVal instead of ByRef.

Confusing, but that's the way it works.

Upvotes: 46

I'm not sure I follow the question or answers, but I'll share this.

Regardless of whether you have a sub-routine of function, defining the parameters passed in ByVal or ByRef will determine if the value of the parameter retains its value outside the sub-routine or function call. If I have the following function:

Function ThisFByRef(ByRef MyValue)
End Function

Anything that I do to the parameter within the function (or sub-routine) will retain its value after the function is complete. ByVal and ByRef are associated with the scope of the sub-routine or function. Any parameter that is passed ByVal will not retain the changes that occur within the called sub-routine or function. Alternately, any parameter that is passed ByRef will retain the value that it was changed to within the sub-routine or function. Returning a value can only be done with a Function and not a Sub-Routine and does not effect the value of the parameter passed in unless the parameter is passed ByRef and changed within the Function. For example:

Dim x
Dim ThisFValue

x = 0
ThisFValue = ThisFByRef(x)
At this point the values would be:
ThisFValue = 2
x = 1

x = 0
ThisFValue = ThisFByVal(x)
At this point the values would be:
ThisFValue = 2
x = 0

Function ThisFByRef(ByRef x)
  x = x + 1
  ThisFByRef = x + 1
End Function

Function ThisFByVal(ByVal x)
  x = x + 1
  ThisFByVal = x + 1
End Function

Upvotes: 0

JunzCode
JunzCode

Reputation: 21

It is simple. When you create a function or sub and you can call them with these way:

For no return value:

myFunction "This is a reference"

For return value:

myValue = myFunction ("This is a reference")

Upvotes: -1

david
david

Reputation: 2638

IncrementByRef Num

calls and increments using a reference to Num

IncrementByRef (47 + 3)

calls and increments using a reference to "50". Which is discarded on exit.

IncrementByRef (Num)
IncrementByRef (Num + 18)*5
IncrementByRef Cint("32")

Are all legal in BASIC, as they were in FORTRAN. Notoriously, in one early FORTRAN, passing by ref allowed you to change the value of expressions like

5

Which was sufficiently confusing that only very small, early FORTRAN compilers had that kind of behaviour.

Upvotes: 4

AnthonyWJones
AnthonyWJones

Reputation: 189495

To be clear. Parentheses have three different purposes.

  1. Used to enclose an argument list when defining or calling procedure
  2. To indicate an indexer on an array.
  3. As an operator in an expression.

There are two ways to call a procedure either as a statement or as an expression.

Expression:-

x = func(y)

Statement:-

func y

Note the Call keyword invokes the procedure as if it were part of an expression hence the argument list must be contained in parantheses.

In the above that y itself represents a very simple expession. We could well have used y + z at this point. In fact we can use any valid expression at this point, including one that uses the parentheses operator. For example:-

 x = (y)

is a valid expression. Hence when you do:-

 func(y)

VBScript sees the call to func to which the result of the expression (y) is passed. Now even if func defines this parameter as ByRef the value in y would be unaffected because y wasn't actually passed as a parameter. What was passed was the result of the expression (y) which would be stored somewhere temporary. Even if this temporary store is modified by func it would be discarded afterwards and hence has the same behaviour had the parameter been marked ByVal.

Upvotes: 9

Related Questions