One of the most important procedural issues is the mechanism used to pass arguments. The examples so far have used the default mechanism: passing arguments by reference. The other mechanism is passing by value. Although most programmers use the default mechanism, it’. important to know the difference between the two mechanisms and when to use each one.
Passing Arguments by Reference
Passing arguments by reference gives the procedure access to the actual variable: The calling procedure passes the address of the variable in memory so that the procedure can change its value permanently. In earlier versions of BASIC and Visual Basic,.this was the only argument-passing mechanism.
Start a new Visual Basic project and enter the following function definition in the Form’s Code window:
Function Add(num1 As Integer, num2 As Integer) As Integer
Add = num1 + num2·
num1 =0
num2 = 0
End Function
This simple function adds two numbers and then sets them to zero.
Next, place a Command button on the Form and enter the following code in the button’s Click event:
Dim A As Integer, B As Integer
A = 10
B = 2
Sum = Add(A, B)
,Debug.Print A
Debug.Print B
Debug.Print Sum
This code displays the following results in the Immediate window:
0
0
12
The changes made to the function’s arguments take effect even after the function has ended. The values of the variables A and B have changed permanently.
Now change the definition of the function by inserting the keyword ByVal before the names of the arguments as follows:
Function Add(ByVal numl As Integer, ByVal num2 As Integer) As Integer
With this change, Visual Basic passes copies of the arguments to the function, The rest of the program remains the same. Run the application, click the Command button, and the following values display in the Immediate window:
10
2
12
The function has changed the values of the arguments, but these changes remain in effect only in the function. The variables A and B in the Command1_Click() subroutine haven’t been affected.
When passing an argument by reference, the argument type must match the declared type. In other words, you can’t pass an Integer value if the procedure expects a Double value, even if they have the same value. For example, if the function Degrees() converts temperature values from Celsius to Fahrenheit, the definition of the function is as follows:
Function Degrees(Celsius as Single) As Single
Degrees = (9 / 5) * Celsius + 32
End Function
This function is usually called as:
CTemp = 37
MsgBox Degrees(CTemp)
If CTemp has been declared as an integer, Visual Basic generates a runtime error and displays the following message:
ByRef argument type mismatch
This error message tells you that the argument you’re passing to the function doesn’t match the function declaration. The function expects a Single value and passed an Integer.
You can get around this problem in two ways. The first is to convert the Integer value to a Single value and then pass it to the function:
MsgBox Degrees(CSng(Clemp))
You can also change the variable’s declaration to a single variable.
The second method is to let Visual Basic make the conversion by enclosing the argument in parentheses:
MsgBox Degrees((CTemp))
Visual Basic converts the value to the type that matches the function’s declaration.
In general, you pass arguments by reference only if the procedure has reason to change its value. If the values of the arguments are required later in the program, you run the risk of changing their values in the procedure. For example, if the Degrees() function changes the value of the CTemp argument, the variable won’t have the same value before and after the call of the Degrees() function.
Passing Arguments by Value
When you pass an argument by value, the procedure sees only a copy of the argument. Even if the procedure changes it, the changes aren’t permanent. The benefit of passing arguments by value is that the argument values are isolated from the procedure, and only the program in which they are declared can change their values.
Passing arguments by value requires a bit of extra typing, since this isn’t the default argument-passing mechanism. For example, to declare that the Degrees() function’s arguments are passed by value, use the ByVal keyword in the argument’s declaration as follows:
Function Degrees(ByVal Ce1sius as Single) As Single
Degrees = (9 / 5) * Celsius + 32
End Function
To see what the ByVal keyword does, add a line that changes the value of the argument in the function:
Function Degrees(ByVal Celsius as Single) As Single
Degrees = (9 / 5) * Celsius + 32
Celsius = 0
End Function
Now call the function as follows:
CTemp = InputBox(‘Enter temperature in dcqrees Celsius’)
MsgBox CTemp & .degrees Celsius are’ & Degrees((CTemp)) & “degrees Fahrenheit’
If the value entered in the InputBox is 32, the following message is displayed:
32 degrees Celsius are 89.6 degrees Fahrenheit
Remove the ByVal keyword from the function’s definition and call the function as follows:
Celsius = 32.0
FTemp = Degrees(Celsius)
MsgBox Celsius & ‘degrees Celsius are ‘ & FTemp & ‘degrees Fahrenheit’
This time the program displays the following message:
0 degrees Celsius are 89.6 degrees Fahrenheit Celsius
When the Celsius argument was passed to the Degrees() function, its value was 32. But the function changed its value, and upon return it was decrease the argument was passed by reference, any changes made by the procedure affected the variable permanently.
Using Optional Arguments
Normally, when you want to call a procedure that expects three arguments, you must pass three arguments to the procedure in the proper order. Suppose you write a function to evaluate math expressions, such as 3*cos(3.11159) + log(10). The function Evaluate() should expect a single argument, the expression to be .evaluated, and return a Double value:
Function Evaluate(express;on As String) As Double
The expression variable is the math formula to be evaluated.
What if you want to evaluate functions with an independent variable X, such as 3+cos(X) + log(10)? You’d have to specify not only the expression, but also the value of X. A function that evaluates all types of expressions should be able to accept one or two arguments.
The alternative of passing a dummy second argument when one is not needed . will also work, but it will make your code more difficult to understand. Visual Basic lets you specify optional arguments with the Optional keyword preceding each optional argument. The definition of the Evaluate() function should be:
Function Evaluate(expression As string, Optional XValue As Double) As Double
Once the first optional argument is specified, all following arguments in the definition must also be optional and declared with the Optional keyword. The Evaluate() function can now be called with one or two arguments:
Debug.Print Evaluate(397 / 3 – 102)
or
Debug. Print Eval~ate(397 / sin(3) – 102)
Now, how does the procedure know which arguments it received and which it didn’t? To find out whether an argument was supplied, use the IsMissing() function in the procedure. If an optional argument is not provided, the argument is actually assigned as a variant with the value of Empty. The example below shows how to test for missing optional arguments using the IsMissing() function. The function Evaluate() should contain a statement such as the following:
If IsMissing(XValue) Then
{process expression without an x value}
Else
{process expression for the specified x value}
End If
Here an example of a generic(function rhat supports optional arguments, It doesn’t actual evaluate math expressions. hut it demonstrates how to use the IsMissing() function to check for missing arguments. First, declare the function as follows:
Function Evaluate~expression As String, Optional XValue As Double) As Double
Double
If IsMissing(XValue) Then
MsgBox ‘Evaluating , & expression
Else
MsgBox ‘Evaluating • & expression & .for x = ‘ & XValue
End If
End Function
Next, call this function as follows:
Evaluate(‘3*x+5’ , 10)
and
Evaluate(‘3*10+5’)
In each case, the Evaluate() function displays a different message. The first call to the function displays the following message:
Evaluating 3*x+5 for x = 10
The second call displays only this expression:
Evaluating 3*x+5
You can also specify a default value for an optional argument. If the Evaluate() function must have a value for the XValue argument, you can define the function as follows:
Function Evaluate(expression As String, Optional XValue = 0)
As Double .
If the function is called without a value for the XValue argument, the Evaluate() function uses the value 0 for the missing argument.
For example, Visual Basic’s financial functions (described in Appendix A on the CD) accept optional arguments and specify default values for any missing arguments. The last argument of most Visual Basic financial functions is an integer that specifies whether loan payments are made at the beginning ‘O! at the end of the period. If you don’t supply this value, these functions assume that payments are due at the end of the period.
The benefit of supplying default values for missing arguments is that your code doesn’t have to check for missing arguments and then take a course of action depending on whether an argument’s value was supplied. Using the default value, your program can proceed as if all arguments were supplied.
Passing an Unknown Number of Arguments
Generally, all the arguments that a procedure expects are listed in the procedure’s definition, and the program that calls the procedure must supply values for all arguments. On Occasions, however, you may not know how many arguments will be passed to the procedure. Procedures that calculate averages, ‘or in general, process a number of values, can accept a few to several arguments whose count is not known at design time. In the past, programmers had to pass arrays with the data to similar procedures. Visual Basic 5 introduced the ParamArray keyword, which allowed you to pass a variable number of arguments to a procedure.
Let’s look at an example. Suppose you want to populate a ListBox control with elements. The method for adding an item to the ListBox control is Addltem and is called as follows:
List1.AddItem ‘new item’
This statement adds the string “new item” to the List! ListBox control,
If you frequently add multiple items to a ListBox control from within your code, you can write either repeated statements or a subroutine that performs this task The following subroutine adds a number of arguments to the Listt control:
Sub AddNamesToList(ParamArray NamesArray()).
For Each x In NamesArray
List1.Addltem x
Next x
End Sub
This subroutine’s argument is an array prefixed with the keyword ParamArray. This array holds.all the parameters passed to the subroutine. To add a number of items to the list.call the AddNamesToList() subroutine as follows:
AddNamesToList ‘Robert’, ‘Manny’, ‘Richard’, ‘Charles’, ‘Madonna”
or
AddNamesToList ‘Mercury’, ‘Earth’, ‘Mars’, ‘Jupiter’
If you want to know the number of arguments actually passed to the procedure, use the UBound() function on the parameter array. The number-of arguments passed to the AddNamesToList() subroutine is determined as follows:
UBound(NamesArray())
Here is another method for scanning all the elements of the parameter array and posting them on a ListBox control:
‘For i = 0 to UBound(NamesArray())
Listl.Addltem NamesArray(i)
Next i
A procedure that accepts an unknown number of arguments relies on the order of the arguments. When calling this procedure, you can omit any number of arguments starting with the last one. To omit some of the arguments that appear first, you must use the corresponding comma. Let’s say you Want to call such a procedure and specify the first, third, and fourth arguments. The procedure must be called as:
ProcName arg1, , arg3, arg4
The arguments to similar procedures are usually of equal stature and then order doesn’t make any difference. A function that calculates the mean or other basic statistics of a set of numbers, or a subroutine that populates a Listbox or Combo Box control, are prime candidates for implementing using this technique. If the procedure accepts a variable number of arguments that aren’t equal in stature, then you should consider the technique described in the following section.
Named Arguments
You’ve learned how to write procedures with optional arguments and how to pass a variable number of arguments to the procedure. The main limitation of the argument-passing mechanism, though, is the order of the arguments. If the first argument is a string and the second argument is a date, you can’t change their order. By default, Visual Basic matches the values passed to a procedure to the declared arguments by their order. That’s why the arguments you’ve seen so far are called positional arguments.
This limitation is lifted by Visual Basic’s capability to specify named arguments. With named arguments, you can supply arguments in any order, because they are recognized by name and not by their order in the list of the procedure’s arguments. Suppose you’ve written a function that effects three arguments: a name, an address,
and an e-mail address:
Function Contact(Name As String, Address As String, Email As String)
When calling this function, you must supply three strings that correspond to the arguments Name, Address, and EmaiI, in that order. However, there’s a safer way to call this function supply the arguments in any order by their names. Instead of calling the Contact function as follows:
Contact(‘Peter Evans’, ‘2020 Palm Ave. Santa Barbara,_ CA 90000’, “[email protected]”)
.you can call this way:
Contactt(Address:=’2020 Pa1m Ave, Santa Barbara, CA 90000′,_ Email:=’[email protected]’, Name:a’Peter Evans’)
The equals sign assigns values to the-names of the arguments. Because the arguments are passed by name, you can supply them in any order.
To test this technique, enter the following function declaration in a Form’s code:
Function Contact(Name As String, Address As String, Email As String)
Debug.Print Name
Debug.Print Address
Debug.Print Email
Contact = ‘OK’
End Function
Then, call the Contact() function from within a button’s Click event with the following statement:
Debug.Print Contact(Address:-‘2020 Palm Ave, Santa Barbara, CA 90000’,
You’ll see the following in the Immediate window:
Peter Evens
2020 Palm Ave, Santa Barbara, CA 9000e
OK
The function knows which value corresponds to which ,argument and can process them the same way that it Processes positional arguments. Notice that the function’s definition doesn’t change whether it’s used with positional or named arguments. The presence is in how you call the function and how you declare it.
Named arguments make code safer and easier to read, but because they require a lot of typing, most programmers don’t use them. Besides, programmers are so used to positional arguments that the notion of naming arguments is like having to declare variables when variants will do. Named arguments are good for situations in which you have optional arguments that require many consecutive commas, which may complicate the code. The methods of the various VBA objects (discussed in Chapter 14) require a large number of arguments and they accept named arguments.