One of the most important topics in implementing your own procedures is the mechanism used to pass arguments. The examples so far have used the default mechanism: passing arguments by value. The other mechanism is passing them by reference. Although most programmers use the default mechanism, it’s important to know the difference between the two mechanisms and when to use each.
By Value versus by Reference
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; in other words, the value of the original variable passed to the procedure isn’t affected. The benefit of passing arguments by value is that the argument values are isolated from the procedure, and only the code segment in which they are declared can change their values. This is the default argument-passing mechanism in Visual Basic 2008.
In VB 6, the default argument-passing mechanism was by reference, and this is something you should be aware of, especially if you’re migrating VB 6 code to VB 2008.
To specify the arguments that will be passed by value, use the ByVal keyword in front of the argument’s name. If you omit the ByVal keyword, the editor will insert it automatically because it’s the default option. To declare that the Degrees() function’s argument is passed by value, use the ByVal keyword in the argument’s declaration as follows:
Function Degrees(ByVal Celsius as Single) As Single
Return((9 / 5) * Celsius + 32)
End Function
Code language: VB.NET (vbnet)
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
Return((9 / 5) * Celsius + 32)
Celsius = 0
End Function
Code language: VB.NET (vbnet)
Now call the function as follows:
CTemp = InputBox("Enter temperature in degrees Celsius")
MsgBox(CTemp.ToString & " degrees Celsius are " & _
Degrees((CTemp)) & " degrees Fahrenheit")
Code language: VB.NET (vbnet)
If you enter the value 32, the following message is displayed:
32 degrees Celsius are 89.6 degrees Fahrenheit
Code language: VB.NET (vbnet)
Replace the ByVal keyword with the ByRef keyword in the function’s definition and call the function as follows:
Celsius = 32.0
FTemp = Degrees(Celsius)
MsgBox(Celsius.ToString & " degrees Celsius are " & FTemp & _
" degrees Fahrenheit")
Code language: VB.NET (vbnet)
This time the program displays the following message:
0 degrees Celsius are 89.6 degrees Fahrenheit
Code language: VB.NET (vbnet)
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 0. Because the argument was passed by reference, any changes made by the procedure affected the variable permanently. As a result, when the calling program attempted to use it, the variable had a different value than expected.
Returning Multiple Values
If you want to write a function that returns more than a single result, you will most likely pass additional arguments by reference and set their values from within the function’s code. The CalculateStatistics() function, shown a little later in this section, calculates the basic statistics of a data set. The values of the data set are stored in an array, which is passed to the function by reference. The CalculateStatistics() function must return two values: the average and standard deviation of the data set. Here’s the declaration of the CalculateStatistics() function:
Function CalculateStatistics(ByRef Data() As Double, _
ByRef Avg As Double, ByRef StDev As Double) As Integer
Code language: VB.NET (vbnet)
The function returns an integer, which is the number of values in the data set. The two important values calculated by the function are returned in the Avg and StDev arguments:
Function CalculateStatistics(ByRef Data() As Double, _
ByRef Avg As Double, ByRef StDev As Double) As Integer
Dim i As Integer, sum As Double, sumSqr As Double, points As Integer
points = Data.Length
For i = 0 To points - 1
sum = sum + Data(i)
sumSqr = sumSqr + Data(i) ˆ 2
Next
Avg = sum / points
StDev = System.Math.Sqrt(sumSqr / points - Avg ˆ 2)
Return(points)
End Function
Code language: VB.NET (vbnet)
To call the CalculateStatistics() function from within your code, set up an array of Doubles and declare two variables that will hold the average and standard deviation of the data set:
Dim Values(99) As Double
‘ Statements to populate the data set
Dim average, deviation As Double
Dim points As Integer
points = Stats(Values, average, deviation)
Debug.WriteLine points & ” values processed.”
Debug.WriteLine ”The average is ” & average & ” and”
Debug.WriteLine ”the standard deviation is ” & deviation
Code language: VB.NET (vbnet)
Using ByRef arguments is the simplest method for a function to return multiple values. However, the definition of your functions might become cluttered, especially if you want to return more than a few values. Another problem with this technique is that it’s not clear whether an argument must be set before calling the function. As you will see shortly, it is possible for a function to return an array or a custom structure with fields for any number of values.
Passing Objects as Arguments
When you pass objects as arguments, they’re passed by reference, even if you have specified the ByVal keyword. The procedure can access and modify the members of the object passed as an argument, and the new value will be visible in the procedure that made the call.
The following code segment demonstrates this. The object is an ArrayList, which is an enhanced form of an array. The ArrayList is discussed in detail later in the tutorial, but to follow this example all you need to know is that the Add method adds new items to the ArrayList, and you can access individual items with an index value, similar to an array’s elements. In the Click event handler of a Button control, create a new instance of the ArrayList object and call the PopulateList() subroutine to populate the list. Even if the ArrayList object is passed to the subroutine by value, the subroutine has access to its items:
Private Sub Button1 Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim aList As New ArrayList()
PopulateList(aList)
Debug.WriteLine(aList(0).ToString)
Debug.WriteLine(aList(1).ToString)
Debug.WriteLine(aList(2).ToString)
End Sub
Sub PopulateList(ByVal list As ArrayList)
list.Add("1")
list.Add("2")
list.Add("3")
End Sub
Code language: VB.NET (vbnet)
The same is true for arrays and all other collections. Even if you specify the ByVal keyword, they’re passed by reference. A more elegant method of modifying the members of a structure from within a procedure is to implement the procedure as a function returning a structure, as explained in the section “Functions Returning Structures,” later in this chapter.