ByVal / ByRef
Understanding the implications of passing parameters by value or by reference is important in ensuring proper use of methods.
A function is declared using code like this:
Public myFunction(ByVal Index As Integer, ByVal BM As Bitmap) as Boolean
This declaration defines the ‘signature’ of the method. The meaning of this is:
Public The function will be visible within this application and (if required) to other applications that have access to this application’s methods and properties. Alternatives are Friend or Private.
Function The method is a function – it will return a result. The alternative is Sub, meaning no result will be returned.
myFunction The function name. This name will be used in code to access the function
ByVal Index as Integer. The first argument in the function parameter list is an integer. It will be referred to within the function using the label ‘Index’. It will be passed to the function by value (see below).
ByVal BM As Bitmap The second argument in the function parameter list is a bitmap object. It will be referred to within the function using the label ‘BM’. It will be passed to the function by value (see below).
as Boolean The function will return a result that is type Boolean. This means that the function result can be used as True or False.
The above function might be used in code like this:
If myFunction(TestVal, myImageObject) Then ...
myFunction will be evaluated using arguments TestVal and myImageObject, and the result will be either True or False.
But what exactly is the meaning of ByVal and ByRef?
If a method argument is specified in the method signature as ByVal, it means that the value of the argument is passed to the method. If the current value of the integer Index is 12, then the first argument is passed as the number 12. The second argument is a reference variable: a reference variable does not have a ‘value’ as such – the variable refers to the object, so its ‘value’ is the reference. When a reference variable is passed ByVal, a copy of the reference is made and that copy is passed to the function. Because the function knows that the argument is a reference type, it expects to see a reference, not an actual value. The function will use this copy of the variable value to reference the object. This means that the function will refer to the same object – a copy of a reference still refers to the original object. Therefore, changes to the object that are made from code inside the function (changes made using the variable ‘BM’) will produce changes to the object in the calling code (changes made using the variable ‘myImageObject’). The two references (the original and the copy) have the same ‘value’, so they refer to the same object.
Both copies of the arguments (the value ’12’ and the copy of the reference to myImageObject) are discarded when the function terminates. In summary:
Value variable passed ByVal – any changes to the local variable are discarded when the method terminates. The variable in the calling routine is unchanged.
Reference variable passed ByVal – there is only one object. Changes made to the object within the method will therefore change the object in the calling routine.
This behaviour is different than what applied in Visual Basic 6 and earlier versions.
If, instead, the arguments were passed ByRef, then references to the original variables are passed. To distinguish these references (the ones created within a function for any variable passed ByRef) let’s call them pointers. If the variable is a value variable, the pointer points to the variable value. If the variable is a reference variable, then the pointer points to the reference to the object. In the case of the first argument – an integer – the pointer points to the location where ’12’ is stored. In the case of the second argument – a reference variable – the pointer points to the location where a reference to the myImageObject object is stored.
Because of the way that these ‘pointers’ are managed within the method, the local variables (the variables used within the function or sub) are the same variable that was passed as an argument (technically, they are always fully de-referenced). Therefore:
Value variable passed ByRef – any changes to the local variable are also made to the variable in the calling routine. Reference variable passed ByRef – any changes to the object referenced by the local variable are also made to the object referenced by the variable in the calling routine.
This behaviour is the same as in Visual Basic 6 and earlier versions.
What this means is that :
1.ByVal should be used unless there is a specific reason not to do so. This reduces the risk of accidentally making changes to the variable that was passed from the calling function. But it doesn’t eliminate that risk, because:
2. Changes to an object referred to by a reference variable passed ByVal will be reflected as changes to the object referenced by the corresponding variable in the calling code.
3. A change to any variable passed ByRef will cause the same change to the corresponding variable in the calling code.
Of course, there are times when you want to write a function so that it can change the value of the variable that was passed to it as an argument. Some procedures require a function (or sub) that needs to make changes to several objects simultaneously – for instance to ensure that consistency is maintained across a number of variables. In this case the method may be deliberately written to pass the arguments ByRef so that the function can make changes to the original object. If you use this technique, use ByRef for any variable where you want changes propagated back to the equivalent variable in the calling routine, as a warning to anyone reading the code that the coding was deliberate.
Note that making changes to a reference variable in the method is not regarded as particularly robust programming, and should be avoided if possible. For instance, putting the associated objects into a structure to pass to the function, and having the function return a structure that contains the updated objects, may be a better way to solve the problem.
A trap for Strings.
Strings are reference variables. They don’t always look like it, but they are. If a string is passed ByVal or ByRef to a function or sub then the variable as used in the function refers to the original string object, and the function or sub can (in theory) make changes to it, and those changes will apply to the string variable that was passed as an argument.
But, some some string operations cause a new string to be created. In fact, almost any operation carried out on a string will create a new string object, discarding the old one. ‘Discarding’ is, of course, relative. If the function changes a string, for instance by adding a new character on the end, then what is actually discarded is the reference to the original string, because strings are reference variables. Whether or not the string object itself gets removed from memory depends on whether or not there are any other references to that object – and of course there is such a reference: the original variable that was used as the argument in the function call is still a valid variable in the code that called the function, and so the original string object is not removed. But the variable used in the sub or function now points to a new string.
So what hapens if a function changes a string that has been passed ByVal? Nothing. Passing a variable (the reference) ByVal creates a copy that is used within the method. When the string is changed a new string is created. The copy of the reference to the old string that the function was using is updated to point to the new string. But this reference is only a copy, so when the function ends, the reference to the new string is discarded (as are all the copies of variables passed ByVal). The new string object loses its only reference, and is garbage collected. The string object in the calling routine is unchanged.
Note that this behaviour is not unique to strings. Whenever a new object is assigned to a reference variable, it is the reference that gets updated, not the object. Updating the reference means that the reference no longer points to the original object, so any changes made are not reflected in that original object. The difference with strings is that it’s not always apparent when a new string is created, but a good rule is that a new string is created whenever the string is changed. Setting an object to Null is also changing the reference of a reference variable, so trying to do that in a method will also result in no change to the object in the calling code. Any change that means that the reference is changed, not the object itself, means that the change will not propogate back to the variable that was passed in ByVal.
And what happens if a function changes a string that has been passed in ByRef? As for any reference type, a change made in the method will be a change to the object that the variable refers to – changes made in the method will appear as changes in the calling routine. But, this is also the case if the change creates a new object. The reference variable ‘pointed to’ by the variable used in the method gets updated to refer to the new object, so the object referenced from the variable in the calling routine is also the new object, and the change appears. So for reference items passed ByRef, the result is the same whether the ‘change’ means an alteration to the state of the object, or the creation of a new object.
The answer is (1) avoid the need for passing method parameters ByRef if possible and (2) never pass strings ByVal and expect the original string object to be correctly updated.
The following simple example demonstrates the results for a string object.
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Src As String Src = Label1.Text UpdateByVal(Src) Label4.Text = Src Src = Label1.Text UpdateByRef(Src) Label5.Text = Src End Sub Sub UpdateByVal(ByVal S As String) S = S & " - Updated!" Label2.Text = S End Sub Sub UpdateByRef(ByRef S As String) S = S & " - Updated!" Label3.Text = S End Sub End Class