Structure Instantiation Details


Structures handle instantiation somewhat differently from object references. When you declare a reference variable, Visual Basic does not automatically allocate the object to which the variable points. On the other hand, when you declare a value type such as a structure, Visual Basic automatically allocates space for the variable’s data. That means you never need to use the New keyword to instantiate a structure.

However, the Visual Basic compiler warns you if you do not explicitly initialize a structure variable before using it. To satisfy the compiler, you can use the New keyword to initialize the variable when you declare it.

A structure can also provide constructors, and you can use those constructors to initialize the structure. The following code defines the SPerson structure and gives it a constructor that takes two parameters, the second optional:

  Public Structure SPerson     Public FirstName As String     Public LastName As String     Public Sub New( _      ByVal first_name As String, _      Optional ByVal last_name As String = "<unknown>")         FirstName = first_name         LastName = last_name     End Sub End Structure  

To use a structure’s constructor, you initialize the structure with the New keyword much as you initialize a reference variable. The following code allocates a SPerson structure variable using the two-parameter constructor:

  Dim artist As New SPerson("Sergio", "Aragones") 

You can also use structure constructors later to reinitialize a variable or set its values, as shown here:

  ' Allocate the artist variable. Dim artist As SPerson ' Do something with artist. ... ' Reset FirstName and LastName to Nothing. artist = New SPerson ... ' Set FirstName and LastName to Bill Amend. artist = New SPerson("Bill", "Amend") 

Structure and class constructors are very similar, but there are some major differences. A structure cannot declare a constructor that takes no parameters. It also cannot provide a constructor with all optional parameters, because that would allow the program to call it with no parameters. Visual Basic always allows the program to use a default empty constructor to declare a structure variable, but you cannot make it use your empty constructor. Unfortunately, that means you cannot guarantee that the program always initializes the structure’s values as you can with a class. If you need that feature, you should use a class instead of a structure.

You also cannot provide initialization values for variables declared within a structure as you can with a class. That means you cannot use this technique to provide default values for the structure’s variables.

The following code demonstrates these differences. The CPerson class defines initial values for its FirstName and LastName variables, provides an empty constructor, and provides a two-parameter constructor. The SPerson structure cannot define initial values for FirstName and LastName and cannot provide an empty constructor.

  ' Class. Public Class CPerson     Public FirstName As String = "<unknown>"    ' Initialization value allowed.     Public LastName As String = "<unknown>"     ' Initialization value allowed.     ' Empty constructor allowed.     Public Sub New()     End Sub     ' Two-parameter constructor allowed.     Public Sub New(ByVal first_name As String, ByVal last_name As String)         FirstName = first_name         LastName = last_name     End Sub End Class ' Structure. Public Structure SPerson     Public FirstName As String ' = "<unknown>" ' Initialization value NOT allowed.     Public LastName As String ' = "<unknown>"  ' Initialization value NOT allowed.     '' Empty constructor NOT allowed.     'Public Sub New()     'End Sub     ' Two-parameter constructor allowed.     Public Sub New(ByVal first_name As String, ByVal last_name As String)         FirstName = first_name         LastName = last_name     End Sub End Structure  

Garbage Collection

When a program starts, the system allocates a chunk of memory for the program called the managed heap. When it allocates data for reference types (class objects), Visual Basic uses memory from this heap. (For more information about the stack and heap and their relative performance, see the section “Heap and Stack Performance” earlier in this chapter.)

When the program no longer needs to use a reference object, Visual Basic does not mark the heap memory as free for later use. If you set a reference variable to Nothing so that no variable points to the object, the object’s memory is no longer available to the program, but Visual Basic does not reuse the object’s heap memory, at least not right away.

The optimizing engine of the garbage collector determines when it needs to clean up the heap. If the program allocates and frees many reference objects, a lot of the heap may be full of memory that is no longer used. In that case, the garbage collector will decide to clean house.

When it runs, the garbage collector examines all the program’s reference variables, parameters that are object references, CPU registers, and other items that might point to heap objects. It uses those values to build a graph describing the heap memory that the program can still access. It then compacts the objects in the heap and updates the program’s references so that they can find any moved items. The garbage collector then updates the heap itself so that the program can allocate memory from the unused portion.

When it destroys an object, the garbage collector frees the object’s memory and any managed resources it contains. It may not free unmanaged resources, however. You can determine when and how an object frees its managed and unmanaged resources by using the Finalize and Dispose methods.

Finalize

When it destroys an object, the garbage collector frees any managed resources used by that object. For example, suppose that an unused object contains a reference to an open file stream. When the garbage collector runs, it notices that the file stream is inaccessible to the program, so it destroys the file stream as well as the object that contains its reference.

However, suppose that the object uses an unmanaged resource that is outside of the scope of objects that Visual Basic understands. For example, suppose the object holds an integer representing a file handle, network connection, or channel to a hardware device that Visual Basic doesn’t understand. In that case, the garbage collector doesn’t know how to free that resource.

You can tell the garbage collector what to do by overriding the class’s Finalize method, which is inherited from the Object class. The garbage collector calls an object’s Finalize method before permanently removing the object from the heap. Note that there are no guarantees about exactly when the garbage collector calls this method, or the order in which different objects’ methods are called. Two objects’ Finalize methods may be called in either order even if one contains a reference to the other or if one was freed long before the other. If you must guarantee a specific order, you must provide more specific clean-up methods of your own.

The following code demonstrates the Finalize method. The Form1 class defines the public variable Running. It then defines the Junk class, which contains a variable referring to the Form1 class. This class’s constructor saves a reference to the Form1 object that created it. Its Finalize method sets the Form1 object’s Running value to False.

When the user clicks the form’s Go button, the btnGo_Click event handler sets Running to True and starts creating Junk objects, passing the constructor this form as a parameter. The routine keeps creating new objects as long as Running is True. Note that each time it creates a new object, the old object that the variable new_obj used to point to becomes inaccessible to the program so it is available for garbage collection.

Eventually the program’s heap runs low, so the garbage collector executes. When it destroys one of the Junk objects, the object’s Finalize subroutine executes and sets the form’s Running value to False. When the garbage collector finishes, the btnGo_Click event handler sees that Running is False, so it stops creating new Junk objects. It displays the number of the last Junk object it created and is done.

  Public Class Form1     Public Running As Boolean     Private Class Junk         Public MyForm As Form1         Public Sub New(ByVal my_form As Form1)             MyForm = my_form         End Sub         ' Garbage collection started.         Protected Overrides Sub Finalize()             ' Stop making objects.             MyForm.Running = False         End Sub     End Class     ' Make objects until garbage collection starts.     Private Sub btnGo_Click(ByVal sender As System.Object, _      ByVal e As System.EventArgs) Handles btnGo.Click         Running = True         Dim new_obj As Junk         Dim max_i As Long         For i As Long = 1 To 100000             new_obj = New Junk(Me)             If Not Running Then                 max_i = i                 Exit For             End If         Next i         MessageBox.Show("Allocated " & max_i.ToString & " objects")     End Sub End Class  

In one test, this program created 30,456 Junk objects before the garbage collector ran. In a second trial run immediately after the first, the program created 59,150 objects, and in a third it created 26,191. The garbage collector gives you little control over when it finalizes objects.

Visual Basic also calls every object’s Finalize method when the program ends. Again, there are no guarantees about the exact timing or order of the calls to different objects’ Finalize methods.

The following example code tests the Finalize method when the program ends. The Numbered class contains a variable m_Number and initializes that value in its constructor. Its Finalize method writes the object’s number in the Output window. The btnGo_Click event handler creates a new Numbered object, giving it a new number. When the event handler ends, the new_numbered variable referring to the Numbered object goes out of scope, so the object is no longer available to the program. If you look at the Output window at this time, you will probably find that the program has not bothered to finalize the object yet. If you click the button several times and then close the application, Visual Basic calls each object’s Finalize method. If you click the button five times, you should see five messages displayed by the objects’ Finalize methods.

  Public Class Form1     Private Class Numbered         Private m_Number As Integer         Public Sub New(ByVal my_number As Integer)             m_Number = my_number         End Sub         ' Garbage collection started.         Protected Overrides Sub Finalize()             ' Display the object's number.             Debug.WriteLine("Number: " & m_Number)         End Sub     End Class     ' Make objects until garbage collection starts.     Private Sub btnGo_Click(ByVal sender As System.Object, _      ByVal e As System.EventArgs) Handles btnGo.Click         Static i As Integer = 0         i += 1         Dim new_numbered As New Numbered(i)         Me.Text = i.ToString     End Sub End Class  

If your class allocates unmanaged resources, you should give it a Finalize method to free them.

Dispose

Because Visual Basic doesn’t keep track of whether an object is reachable at any given moment, it doesn’t know when it can permanently destroy an object until the program ends or the garbage collector reclaims it. That means the object’s memory and resources may remain unused for quite a while. The memory itself isn’t a big issue. If the program’s heap runs out of space, the garbage collector runs to reclaim some of the unused memory.

If the object contains a reference to a resource, however, that resource is not freed until the object is finalized. That can have dire consequences. You generally don’t want control of a file, network connection, scanner, or other scarce system resource left to the whims of the garbage collector.

By convention, the Dispose subroutine frees an object’s resources. Before a program frees an object that contains important resources, it can call that object’s Dispose method to free them explicitly.

To handle the case where the program does not call Dispose, the class should also free any unmanaged resources that it holds in its Finalize subroutine. Because Finalize is executed whether the program calls Dispose or not, it must also be able to execute both the Dispose and Finalize subroutines without harm. For example, if the program shuts down some piece of unusual hardware, it probably should not shut down the device twice.

To make building a Dispose method a little easier, Visual Basic defines the IDisposable interface, which declares the Dispose method. If you enter the statement Implements IDisposable and press Enter, Visual Basic creates an empty Dispose method for you.

The following code demonstrates the Dispose and Finalize methods. The Named class has a Name variable that contains a string identifying an object. Its Finalize method simply calls its Dispose method. Dispose uses a static variable named done_before to ensure that it only performs its task only once. If it has not already run, the Dispose method displays the object’s name. In a real application, this method would free whatever resources the object holds. Whether the program explicitly calls Dispose, or whether the garbage collector calls the object’s Finalize method, this code is executed exactly once.

The main program has two buttons labeled Dispose and No Dispose. When you click the Dispose button, the btnDispose_Click event handler makes a Named object, giving it a new name, and then calls the object’s Dispose method, which immediately displays the object’s name.

When you click the No Dispose button, the btnNoDispose_Click event handler makes a new Named object with a new name and then ends without calling the object’s Dispose method. Later, when the garbage collector runs or when the program ends, the object’s Finalize method executes and calls

  Public Class Form1     Private Class Named         Implements IDisposable         ' Save our name.         Public Name As String         Public Sub New(ByVal new_name As String)             Name = new_name         End Sub         ' Free resources.         Protected Overrides Sub Finalize()             Dispose()         End Sub         ' Display our name.         Public Sub Dispose() Implements System.IDisposable.Dispose             Static done_before As Boolean = False             If done_before Then Exit Sub             done_before = True             Debug.WriteLine(Name)         End Sub     End Class     ' Make an object and dispose it.     Private Sub btnDispose_Click(ByVal sender As System.Object, _      ByVal e As System.EventArgs) Handles btnDispose.Click         Static i As Integer = 0         i += 1         Dim obj As New Named("Dispose " & i)         obj.Dispose()     End Sub     ' Make an object and do not dispose it.     Private Sub btnNoDispose_Click(ByVal sender As System.Object, _      ByVal e As System.EventArgs) Handles btnNoDispose.Click         Static i As Integer = 0         i += 1         Dim obj As New Named("No Dispose " & i)     End Sub End Class  

If your class allocates managed or unmanaged resources and you don’t want to wait for the garbage collector to get around to freeing them, you should implement a Dispose method and use it when you no longer need an object.

Constants, Properties, and Methods

Declaring constants, properties, and methods within a class is the same as declaring them outside a class. The main difference is that the context of the declaration is the class rather than a namespace. For example, a variable declared Private within a class is available only to code within the class.

For information on declaring variables and constants, see Chapter 4. For information on declaring methods, see Chapter 6, which also describes property procedures, special routines that implement a property for a class.

One issue that is sometimes confusing is that the unit scope of a class is the class’s code, not the code within a specific instance of the class. If you declare a variable within a class Private, then all code within the class can access the variable, whether or not that code belongs to the instance of the object that contains the variable.

For example, consider the following Student class. The m_Scores array is Private to the class, so you might think that a Student object could only access its own scores. In fact, any Student object can access any other Student object’s m_Scores array as well. The CompareToStudent subroutine calculates the total score for the current Student object. It then calculates the total score for another student and displays the results.

  Public Class Student     Public FirstName As String     Public LastName As String     Private m_Scores() As Integer     ...     Public Sub CompareToStudent(ByVal other_student As Student)         Dim my_score As Integer = 0         For i As Integer = 0 To m_Scores.GetUpperBound(0)             my_score += m_Scores(i)         Next i         Dim other_score As Integer = 0         For i As Integer = 0 To other_student.m_Scores.GetUpperBound(0)             other_score += other_student.m_Scores(i)         Next i         Debug.WriteLine("My score:    " & my_score)         Debug.WriteLine("Other score: " & other_score)     End Sub     ... End Class  

Breaking the encapsulation provided by the objects in this way can lead to unnecessary confusion. It is generally better to try to access an object’s Private data only from within that object. Usually, you can provide access routines that make using the object’s data easier to understand.

The following version of the Student class includes a TotalScore function that returns the total of a Student object’s scores. This function works only with its own object’s scores, so it does not pry into another object’s data. The CompareToStudent subroutine uses the TotalScore function to display the total score for its object and for a comparison object.

  Public Class Student     Public FirstName As String     Public LastName As String     Private m_Scores() As Integer     ...     Public Sub CompareToStudent(ByVal other_student As Student)         Debug.WriteLine("My score:    " & TotalScore())         Debug.WriteLine("Other score: " & other_student.TotalScore())     End Sub     ' Return the total of this student's scores.     Private Function TotalScore() As Integer         Dim total_score As Integer = 0         For i As Integer = 0 To m_Scores.GetUpperBound(0)             total_score += m_Scores(i)         Next i         Return total_score     End Function     ... End Class 

Function TotalScore is itself declared Private, so only code within the class can use it. In this example, the CompareToStudent subroutine calls another object’s Private TotalScore function, so the separation between the two objects is not absolute, but at least CompareToStudent doesn’t need to look directly at the other object’s data.




Visual Basic 2005 with  .NET 3.0 Programmer's Reference
Visual Basic 2005 with .NET 3.0 Programmer's Reference
ISBN: 470137053
EAN: N/A
Year: 2007
Pages: 417

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net