KnowDotNet NetRefactor

Understand Types in .NET

by William Ryan
Print this Article Discuss in Forums

It's very easy to work with .NET and not understand why things work they way they do.  In some areas it may not be a big deal, in other areas it's huge.  When I first moved to .Net, I thought Reference and Value types were effectively the same thing as Passing something by Reference or Value.  If I passed it by value, I passed a copy that couldn't be changed.  If I passed it by Reference, it could be changed.  This was a BIG mistake and I paid for it a few times.  Hopefully, I can help you avoid some of the same mistakes I made...

ust the other day, I was reading a post that Visual Studio .NET doesn't work like the documentation says it should.  The person in this post was declaring multiple System.Data.DataColumn objects and sometime thereafter was setting them to Nothing.  According to him, when you set something to Nothing in .NET, the documentation says it will return to its default value.  However, after setting his DataColumn objects to nothing, when he tried referencing them again, he was throwing exceptions wherein he was expecting to see the default values of the DataColumn.  For instance, DataColumn1.DefaultValue = 1.  Somewhere thereafter he set a given Row's Column1 value to say 50.  Then he set it to nothing.  He was expecting to see a Value of 1 the next time he checked it.  Instead, he was getting an unhandled exception.  I posted a reply indicating that his problem was that he was setting reference type to nothing, which in fact does set it back to its default value, which is nothing.  (If you declare a reference type and never instantiate it or set it's value, it will remain nothing).  As such, the DefaultValue of a given instance of a DataColumn is not the same as the default value of the DataColumn object itself.  He proceeded to post the MS documentation (which is very clear on this subjects) and showed me 3 or 4 examples proving I was wrong and so was Microsoft.  Each of his examples declared a Value type, set its value to nothing, and then did  a Debug.Writeline(variable.ToString) to prove his point.   So I used this to illustrate the problem:

Private Structure Bill
Dim i As Integer
        Dim x As Integer

Sub Button2_Click(..event handler stuff) Handles Button2.Click
Dim i As Integer = 24
    Debug.WriteLine(i.ToString) '24
    i =
    Debug.WriteLine(i.ToString) '0
Dim b1 As Bill
    b1.i = 1
    b1.x = 2

   Debug.WriteLine(b1.i.ToString, b1.x.ToString)) ' 1, 2
     b1 =
   Debug.WriteLine(b1.ToString) 'Won't blow up even though its Nothing
   Dim dc As New DataColumn
   dc.DefaultValue = "Bill"
'Will write Bill
   dc = Nothing
   Debug.WriteLine(dc.GetType.ToString) ' Will throw exception

End Sub

So, what's the difference?  In both instances, I declare an object, assign some of its properties, write those properties, set the object to nothing and try to write those properties again.  After my structure was set to nothing and I try to write out its .ToString() implementation, I get:  "WindowsApplication1.Form1+Bill" whereas when I do the same with a DataColumn, I throw an exception.  The difference lies in the fact that Structures for instance, are Value types.  So are integers.  So if I declare an integer, assign 24 to it, set it to nothing, I have an integer with a value of 0, which is the default value of an integer.  If I do the same with a Reference type, I get an exception.

This is just one area where you can get yourself in trouble if you don't understand the differences.  Let's look at another example.  

Dim i As Integer = 24
Dim x As Integer
x = i
Debug.WriteLine(i.ToString & ", " & x.ToString) ' 24, 24
i =
Debug.WriteLine(i.ToString & ", " & x.ToString) ' 0, 24

Now, compare this to this code:

Dim dc As New DataColumn
dc.DefaultValue = "Bill"
Dim dc2 As New DataColumn
dc2.DefaultValue = "NotBill"
'Will Write "NotBill"
dc2 = dc
dc.DefaultValue = "Neither"
'Will write "Neither"
Debug.WriteLine(dc2.DefaultValue) 'Will write "Neither"
dc = Nothing

' Won't throw exception
Debug.WriteLine(dc.GetType.ToString)  'Will Throw an exception

So how can this be? If we change the DefaultValue of dc, it's immediately reflected in dc2, but if we set it to nothing, dc2 still exists, and it won't throw an exception, whereas any reference (pardon the pun) to dc will throw an exception.  To understand this, you need to understand the mechanics of Garbage Collection.  Dc and dc2 both hold references to some point in memory since they are reference types, and we set one equal to the other.  We set dc to nothing, which doesn't change anything at the location in memory, it simply removes the reference  that binds dc to that piece of memory. Dc2 still holds that piece of memory.  Now, to prove this, I added GC.Collect after setting dc to nothing (which in practice, you should have reason to do except in very rare circumstances.  In fact, let me go so far as to say that if you don't understand Garbage Collection intimately, don't Ever call it unless you are experimenting and trying to learn it.):

dc = Nothing

Now, if I run the code, it will behave exactly as it did when I didn't call GC.Collect.  You see, the memory allocated on the managed heap to handle dc isn't freed up as long as dc2 holds a reference to it. (Discussing the various generations of the Garbage Collector is the subject of an article I'm currently working on, but outside of the scope of this one.)

As you can see, if you are using Reference types, there is a lot to consider.  Intuitively, you would guess that when you set dc to Nothing, dc2 would as well be nothing but we proved that isn't the case.  However, if these were value types, the behavior is totally different.

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing