Structures Change From Value Types to Reference Types When Boxed
Another Vote for Option Strict
Why can't I change a value in a Structure when it is in an array, ArrayList, or Collection? The problem concerns understanding a .NET concept called Boxing. My partner William Ryan has already written an article on Boxing, but I recently ran into a problem with it and thought it deserved reinforcement.
A data type is a value type if it holds data within its own memory allocation. A reference type contains a pointer to another memory location that hold the data. Value types include numeric data (which includes Byte, Short, Integer, and Long), Boolean, Char, and Date. Reference types include String and arrays, which includes Collections and ArrayLists, even if their elements are value types.
By the definitions described previously, Stucture (struct in C#) are value type, unless they are placed within an array, ArrayList, or Collection. At that point, the structure morphs to a reference type. If you are not aware of this and you are not in the habit of using Option Strict On in VB.NET, you will have problems at run-time. For example, consider the following code.
Private Structure Regions
Dim RName As String
Dim RIndex As Integer
Dim Usable As Boolean
End Structure
|
Public Shared RegionList As ArrayList
|
Public Shared Sub EnumerateNewRegions()
|
' dimension an instance of the struct
Dim RI As Regions
|
RI.RName = dr(0)
RI.RIndex = 0
RI.Usable = True
|
At this point,RI, the instance of Structure, is a value type, as are all of it's properties. You can still read or write to any of the properties of RI. Now consider the next line of code and the result of it.
Since RegionList is an ArraList, and ArrayLists are reference types, RI, which previously was value type, has now become a reference type, becuause it is a part of a reference type. This is called Boxing, and the structure instance RI is now Boxed. So what does all that mean to me as a progrmmer? Consider the next line of code.
CRegions.RegionList(i).Usable = False
|
First, if you are using Option Strict, you will get a compiler error, on the previous line of code, saying that "Option Strict does not permit late binding." But, if you do not use Option Strict, you will not get a compile error, and the preceding line of code will not work at run-time. Furthermore, it will not cause a run-time error, unless of course you really were counting on the .Usable property being set to False! Since the variable will not be set to True, then it is a Bug, which is a run-time error of the most devious kind.
Why did this happen? Remember that the structure, RI, and all of its properties morphed into reference types, or pseudo object variables. Also remember that an Object variable always holds a pointer to the data, never the data itself. So, attempting to change the .Usable property will fail and will not raise an exception.
What is the solution? First, use Option Strict. Obviously, Option Strict will require additional coding, and absolute adherence to stricter coding rules, but it will pay off. Late binding will not be allowed, but that is something that your need to get away from anyway. However, if you are not in the habit of using Option Strict, and you are in the habit of using late binding, turning on Option Strict could be a culture shock. You may find that a Class you have been using, without any trouble, suddenly contains 35 compile errors.
There is another solution, that you can take in the short term, but you should consider doing it right and use Option Strict. The code shown below will allow you to get around the problem.
Dim oneRegion As Regions
For i = 0 To RegionList.Count - 1
' Un box the structure
oneRegion = CRegions.RegionList(i)
' change the property
oneRegion.Usable = True
' put the structure back in the box
CRegions.RegionList(i) = oneRegion
Next
|
The code shown above unboxes the boxed instance of the Structure into an instance named oneRegion. Its properties can now be modified and then the modified structure is placed back in the box. Although this causes a performance hit, it is a temporary solution.
In retrospect, Structures have their place. They work fine when passed around individually, or unboxed. However, if you need a Collection of them, such as in the previous example code, you should use a data class object instead of a structure. Objects are reference types to begin with and had the data variables been placed in a data class instead of a structure, the problem never would have existed.