Coding Arguments

Variables that are local to a procedure can only be used inside that procedure. Variables declared inside a module’s general section are global to the module and available throughout the entire module. Variables declared with Public instead of Dim inside the general section are global to the entire project.

You’ve seen throughout the first part of this book that you should avoid global variables as much as possible and use only local variables. If, however, you only use local variables but you write lots of small procedures (as you should), how can the procedures share data? If all the data is local, a called procedure has no access to the calling procedure’s data. As you probably suspect, you’ll share data through argument lists. When one procedure must call another procedure, and the called procedure needs information from the calling procedure, the calling procedure can send that information inside the argument list.

Suppose one procedure calculates a value and a second procedure must use that value in a different calculation before displaying a result on the form. You need to know how to pass local data from the procedure that defines the local variable to other procedures that need to work with that value.

When you call a built-in function, you pass one or more arguments to the function so that the function’s internal code has data to work with. When you call your own subroutine and function procedures, you also can pass arguments to them. The arguments are nothing more than the passing procedure’s local variables that the receiving procedure needs to work with.

After you pass data, that data is still local to the original passing procedure, but the receiving procedure has the opportunity to work with those values for the time of the procedure execution. Depending on how you pass the arguments, the receiving procedure might even be able to change those values so that when the passing procedure regains control, its local variables have been modified by the called procedure.

note

The passed argument name (or names) doesn't have to be the same as you used in the receiving procedure. Therefore, you might call a subroutine with Call CalcIt(X) and the subroutine begins with this declaration line: Public Sub CalcIt(Y As Int). Although in this case both X and Y refer to the same value, the receiving subroutine procedure uses a different name from the passing procedure. The only argument list requirements are that the calling and receiving argument lists must match in number of arguments and they must match in datatype order.

You must declare the receiving argument list’s datatypes for each argument. If you pass or receive more than one argument, separate the arguments with commas. The following statement passes the three values to a subroutine:

Call RecProc(I, J, K)

The following statement declares the RecProc() procedure:

Public Sub RecProc (I As Integer, J As Integer, K As Single)

The calling procedure already knows the datatypes of I, J, and K, but those values are unknown to RecProc(). Therefore, you’ll have to code the datatype of each received argument so that the receiving function knows the datatype of each sent argument.

If a subroutine or function procedure is to receive arrays, don’t indicate the array subscripts inside the argument list. The following Sub statement defines a general-purpose subroutine procedure that accepts four arrays as arguments:

Public Sub WriteData (GNames() As String, CBalc() As Currency,

ÂCDate() As Variant, CRegion() As Integer)

The built-in UBound() function returns the highest subscript that’s defined for any given array. The following statement, which might appear inside the WriteData() subroutine, stores the highest possible subscript for the CNames() array, so the subroutine won’t attempt to access an array subscript outside the defined limit:

intHighSub = UBound(CNames)

Receiving by Reference and by Value

Visual Basic lets you pass arguments two ways: by reference and by value. The way you use them determines whether the receiving procedure can change the arguments so that those changes remain in effect after the calling procedure regains control. If you pass and receive by reference (the default method), the calling procedure’s passed local variables may be changed in the receiving procedure. If you pass and receive by value, the calling procedure can access and change its received arguments, but those changes don’t retain their effects in the calling procedure.

note

Passing by reference is sometimes called passing by address. In some languages, by address and by reference mean two different things, but not in Visual Basic.

When passing by reference, subroutines and functions can always use their received values and also change those arguments. If a receiving procedure changes one of its arguments, the corresponding variable in the calling procedure is also changed. Therefore, when the calling procedure regains control, the value (or values) that the calling procedure sent as an argument to the called subroutine may be different from the time before the call.

note

By reference is a way in which you pass values and allow the called procedure to change those values, also called by address. By value is a way in which you pass values and protect the calling procedure’s passed data so that the called procedure cannot change the original data.

Arguments are passed by reference, meaning that the passed arguments can be changed by their receiving procedure. If you want to keep the receiving procedure from being able to change the calling procedure’s arguments, you must pass the arguments by value. To pass by value, precede any and all receiving argument lists with the ByVal keyword, or enclose the passed arguments in parentheses.

note

If you want to be explicit, use the ByRef keyword. Passing by reference is the default method if you don’t specify ByRef.

It’s generally safer to receive arguments by value because the calling procedure can safely assume that its passed values won’t be changed by the receiving procedure. Nevertheless, there may be times when you want the receiving procedure to permanently change values passed to it. In such cases, you'll need to receive those arguments by reference.

Listing 13.3 shows two subroutine procedures. One, named Changes(), receives arguments by address. The second procedure, NoChanges(), receives its arguments by value. Even though both procedures multiply their arguments by two, those changes affect the calling procedure’s variables only when Changes() is called but not when NoChanges() is called.

Listing 13.3. Some procedures can change the sending procedure’s arguments.

1: Sub Changes(N As Integer, S As Single)

2: ' Receives arguments by reference

3: N = N * 2 ' Double both

4: S = S * 2 ' arguments

5: ' When the calling routine regains control,

6: ' its two local variables will now be twice

7: ' as much as they were before calling this.

8: End Sub

9:

10: Sub NoChanges(ByVal N As Integer, ByVal S As Single)

11: ' Receives arguments by value

12: N = N * 2 ' Double both

13: S = S * 2 ' arguments

14: ' When the calling routine regains control,

15: ' its two local variables will not be

16: ' changed from their original values.

17: End Sub

As you can see, Changes() receives its arguments by reference. (Remember that the default passing method is by reference, even if you omit ByRef.) Therefore, when the procedure doubles the arguments, the calling procedure’s argument variables change as well.

In NoChanges(), the procedure receives its arguments by value. Therefore, nothing NoChanges() does can change those values in the calling procedure.

Top Home