All the Types in .Net are in one of two categories - they are either value types or reference types.
For our purposes we'll simplify things. We'll say that value types are stored in Stack memory, which is fast but limited in size. Compared to reference types, which are stored in Heap memory, which is slower but larger. (Value types can live on the heap too, if they are part of a reference type, but we are just thinking about variables declared locally inside procedures to keep things simple).
A variable is just a label we use as programmers to identify some value on the stack. Let's look at value types, as I've said they live on the stack...
Value types are all fairly small - they don't use much memory. The numeric types are all value types, and they don't take up much size:
Byte - 1 byte
Short - 2 bytes
Integer - 4 bytes
Long - 8 bytes
Single - 4 bytes
Double - 8 bytes
Decimal - 16 bytes
Structures and Enums are also value types. Arrays and Strings are not!
When we declare a value type:
vb
Dim x As Integer = 100
We can think of it like this:
CODE
Variable Stack Heap
"points" to .-----------.
x -------------->| 100 | not involved
'-----------'
So there's the value
100 living on the stack. It takes up 4 bytes of stack memory as that is the size of an
Integer.
Say we declare a value type, but don't assign a value...
vb
Dim y As Integer
Value types always get a default value. For
Integer that is a
0:
CODE
Variable Stack Heap
"points" to .-----------.
y -------------->| 0 | not involved
'-----------'
What happens if we assign one variable to another:
vb
Dim x As Integer = 100
Dim y As Integer = x
This is simple -
y gets a copy of the value:
CODE
Variable Stack Heap
"points" to .-----------.
x -------------->| 100 | not involved
'-----------'
"points" to .-----------.
y -------------->| 100 | not involved
'-----------'
This means that
x and
y are independent. If you now add one to
y, you won't change
x.
Ok, enough on Value types. Reference types are Objects. They live on the heap. But the sneaky part is that variables still point to something on the stack. That something is a reference to the heap. You can think of it as the heap address where the object is stored.
Button is a reference type, so here we create one and assign it to a variable.
vb
Dim button1 As New Button
CODE
Variable Stack Heap
.-----------. .---------.
button1 ----------->|heap |------------->| button | &H1
|address &H1| | object |
'-----------' '---------'
Here I've used an imaginary address &H1 as the heap address. So the variable points to the stack, which has the heap address &H1 which is where the Button lives on the heap.
If we declare, but don't assign an object, by omitting the
New part:
vb
Dim button2 As Button
Then we do not create the object on the Heap, but we do have the variable. The variable DOES point to something on the stack too. That something is the null reference - in VB we call it
Nothing. Just as
Integer has a default value of
0, references have a default value of
Nothing.
CODE
Variable Stack Heap
.-----------.
button2 ----------->| Nothing | Not involved
'-----------'
So, if you tried to do something with
button2, you would get the "Object reference not set to an instance blah blah" error. Because, the stack stores the special
Nothing value which doesn't point to a
Button object on the heap.
Again, lets look at what happens when you assign the value of one object variable to another:
vb
Dim button3 As New Button
Dim button4 As Button = button3
Now - if
Button was a value type then we would get a copy. And, in a way, we do get a copy - but it is a copy of the address. So, the net result is that both variables point to the same object on the heap:
CODE
Variable Stack Heap
.-----------. .---------.
button3 ----------->|address on |------------->| button |
| heap &H1| | | &H1
'-----------' '---------'
.-----------. ----------------^
button4 ----------->|address on |
| heap &H1|
'-----------'
The result of this is that if you can change the same object using either variable.
If you now decided to say
button4 = New Button then
button4 would now "reference" the new button, whilst
button3 would reference the old button still.
So, what happens when you call a Sub ByVal?
Well, you are sending a copy of the value on the stack. For a value type it is the value, for a reference type it is a reference to an object on the heap...
Value Type ByVal: You send a copy of the value, and the original value will not be changed by changes made in the sub.
vb
' Calling code:
Dim x As Integer = 100
AddOne(x)
' ...
' The Sub
Sub AddOne(ByVal foo As Integer)
foo+=1
End Sub
Imagine we are in the sub, just as it starts to run. X still exists back in the calling code, and foo has a copy of the value on the stack that x points to:
CODE
Variable Stack Heap
.-----------.
x ----------->| 100 |
'-----------'
.-----------.
foo ----------->| 100 |
'-----------'
You can see that the two are independent- the variables are pointing to different stack locations. Changes won't stick.
Value Type ByRef: This time the variable in the local sub, used in the sub's signature, will point to the same stack location. So, if you change the value in the sub, you are changing the original.
vb
' Calling code:
Dim x As Integer = 100
AddOne(x)
' ...
' The Sub
Sub AddOne(ByRef foo As Integer)
foo+=1
End Sub
CODE
Variable Stack Heap
.-----------.
x ----------->| 100 |
foo ----------->| |
'-----------'
They are both pointing to the same value on the stack, so changes will stick.
Reference Type ByVal: You send a copy of the Stack contents - i.e. the address to the object on the heap. So, if you alter the object - say be setting a property to a new value, then it is the same object as the one in the calling code - it will be updated.
vb
' Calling code:
Dim button1 As New Button
SetText(button1)
' ...
Sub SetText(ByVal foo As Button)
foo.Text = "foobar"
End Sub
This should look familiar:
CODE
Variable Stack Heap
.-----------. .---------.
button1 ----------->|address on |------------->| button |
| heap &H1| | | &H1
'-----------' '---------'
.-----------. ----------------^
foo ----------->|address on |
| heap &H1|
'-----------'
So - they are both referencing the same object. Therefore when the property is changed in the sub, it will be changed in the calling code. Changes Stick.
Reference Type ByRef: Just like the value types, the local variable in the sub will point to the same stack location:
CODE
Variable Stack Heap
.-----------. .---------.
button1 ----------->|address on |------------->| button |
foo ----------->| heap &H1| | | &H1
'-----------' '---------'
Again, if the variables reference the same button object and changes will stick!
So - for Reference types if we pass ByVal or ByRef the object in the calling code can be modified. Is there a difference!?!?!
YES (and this is an interview question!):
Consider:
vb
' Calling code
Dim button1 As New Button
Bar(button1)
' ...
Sub Bar(ByVal foo As Button)
foo = New Button
End Sub
Here we are doing something different - we are not altering the object on the heap by setting a property, but assigning a completely new object to the variable. This is the result when execution has just passed the line
foo = New ButtonCODE
Variable Stack Heap
.-----------. .---------.
button1 ----------->|address on |------------->| button | &H1
| heap &H1| | object |
'-----------' '---------'
.-----------. .---------.
foo ----------->|address on |------------->| button | &H2
| heap &H2| | object |
'-----------' '---------'
Here the change hasn't stuck. The
foo variable that is local to the sub references the new
Button at the new address, but the old
button1 variable remains the same, pointing to address &H1 which has the original
Button object.
Finally... (congratulations on getting this far BTW)
Consider the ByRef version of that:
vb
' Calling code
Dim button1 As New Button
Bar(button1)
' ...
Sub Bar(ByRef foo As Button)
foo = New Button
End Sub
CODE
Variable Stack Heap
.-----------. .---------.
button1 ----------->|address on | | button | &H1
foo ----------->| heap &H2|--- | object |
'-----------' | '---------'
| .---------.
|--------->| button | &H2
| object |
'---------'
foo and
button1 both point to the same stack address. So once a
New Button has been assigned, they both reference the
New Button at &H2. The
Button object at &H1 is no longer referenced by any variable and it is therefore freed for garbage collection.
See:
MSDN: Passing Arguments by Value and by ReferenceC# Heap(ing) Vs Stack(ing) in .Net - the non-simple way to explain it all, and more, but in C#...