Introduction
This is a common question asked on nearly every forum I have seen, and while the concept may seem simple, a quick look beneath the surface exposes some interesting things to remember:
Rule 1: When passing an Object (Reference Type) ByVal, a reference to the Object (not the variable) is passed.
Rule 2: A variable (Value Type) passed ByVal is not affected by the code inside the method, while a variable passed ByRef is.
Example 1: Value Types
To demonstrate, build and run this simple Console Application:
Public Module1
Public Sub DoSomething(ByVal x As Integer, ByRef y As Double)
x += 5
y += 5
Console.WriteLine("Inside Method Call:")
Console.WriteLine("num= {0}", x.ToString)
Console.WriteLine("anum = {0}", y.ToString)
End Sub
Sub Main()
Dim num As Integer = 5
Dim anum As Double = 20.5
Console.WriteLine("Before Method Call:")
Console.WriteLine("num = {0}", num.ToString)
Console.WriteLine("anum = {0}", anum.ToString)
DoSomething(num, anum)
Console.WriteLine("After Method Call:")
Console.WriteLine("num = {0}", num.ToString)
Console.WriteLine("anum = {0}", anum.ToString)
Console.Read()
End Sub
End Module
Result:
Before Method Call:
num = 5
anum = 20.5
Inside Method Call:
num = 10
anum = 25.5
After Method Call:
num = 5
anum = 25.5
Note that in the code, we set two variables and initialize them:
Dim num as Integer = 5,
Dim anum as Double = 20.5
After displaying the initialized values, we call the method DoSomething()
, supplying our variables as parameters. num
is passed ByVal and anum
is passed ByRef.
Check the results on your screen to see what has happened.
Because num
was passed ByVal, no changes were made to the variable - a copy of the variable was passed into the method.
And, as anum
was passed ByRef, any changes made in the method body will still be in effect after the method has completed.
Example 2 - Reference Types
Now that you have seen what happens when you pass value types ByVal
and ByRef
, let's see what happens when we pass Reference Types (objects).
Passing a value type to a method by value is equivilant to instantiating a new variable and assigning to it the first variable. This also applies to Reference Types.
Create a new console project:
Module Module1
Private Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New(ByVal name As String)
_name = name
End Sub
End Class
Private Sub ByValRefType(ByVal p As Person)
p.Name = "Francis"
Console.WriteLine("Inside method p = {0}", p.Name)
p = Nothing
End Sub
Sub Main()
Dim p As Person = New Person("Jason")
Console.WriteLine("Before Method Call p = {0}", p.Name)
ByValRefType(p)
Console.WriteLine("After Method Call p = {0}", p.Name)
Console.ReadLine()
End Sub
End Module
Running this sample gives the following result:
Before Method Call p = Jason
Inside method p = Francis
After Method Call p = Francis
The referenced memory was changed (to Francis) but the p = Nothing
statement had no effect on our originally declared variable p
.
This is in contrast to when a Reference Type is passed ByRef...
Module Module1
Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
Public Sub DoSomething(ByVal person1 As Person, _
ByRef person2 As Person)
person1.Name = "Changed Name"
person2.Name = "Also Changed"
Console.WriteLine("Inside Method:")
Console.WriteLine("Person1 Name: {0}", person1.Name)
Console.WriteLine("Person2 Name: {0}", person2.Name)
person2 = New Person("Abel Tasman")
Console.WriteLine("Person2 Name: {0}", person2.Name)
Console.WriteLine()
Person1 = Nothing
Person2 = Nothing
End Sub
Sub Main()
Dim p1 As New Person("Captain Cook")
Dim p2 As New Person("Vasco da Gama")
Console.WriteLine("Before Method Call:")
Console.WriteLine("Person1 Name: {0}", p1.Name)
Console.WriteLine("Person2 Name: {0}", p2.Name)
Console.WriteLine()
DoSomething(p1, p2)
Console.WriteLine("After Method Call:")
Console.WriteLine("Person1 Name: {0}", p1.Name)
Console.WriteLine("Person2 Name: {0}", p2.Name)
Console.Read()
End Sub
End Module
Result:
Before Method Call:
Person1 Name: Captain Cook
Person2 Name: Vasco da Gama
Inside Method:
Person1 Name: Changed Name
Person2 Name: Also Changed
Person2 Name: Abel Tasman
After Method Call:
Person1 Name: Changed Name
' NullReferenceException here
We passed two Person
parameters to the method DoSomething()
:
Dim p1 As New Person("Captain Cook")
Dim p2 as New Person("Vasco da Gama")
In the call to the method, the first argument p1
is passed ByVal, while the second argument p2
is passed ByRef as in the first example.
As the results show, When the parameter p2
is passed ByRef to the method (as Person2
)and the memory is deallocated in the method body, the reference to Person
for p2
is also deallocated. Hence the NullReferenceException when we tried to access the deallocated memory:
Console.WriteLine("Person2 Name: {0}", p2.Name)
Note also that the person2 = New Person("Abel Tasman")
assignment in the method also points to the same memory location.
The result of the person1 = Nothing
statement in the method is predictable and doesn't affect our p1
variable.
An Additional Thought
The beauty of passing a value type ByRef is that you can in-effect make a Sub
into a "Function without a declared Return Type", and return more than one value from a Function!
History
- 29th November, 2009: Initial post
- 30th November, 2009: Article updated
- 21st February, 2010: Article updated