ByVal / ByRef

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
   Label4.Text = Src
   Src = Label1.Text
   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
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s