Library tutorials & articles
New Object-Oriented Capabilities in VB.NET
Object Lifecycle
Object Lifecycle
In VB6, objects had a clearly defined and well-understood life cycle – a set of events that we always knew would occur over the life of an object. We were guaranteed the following:
|
Event |
Description |
|
Sub Main |
Would run as the component was loaded, before an object was created (optional) |
|
|
Would run before any other code in our object; called by the runtime as the object was being created |
|
Event |
Description |
|
|
Would run after any other code in our object; called by the runtime as the object was being destroyed |
With VB.NET, objects also have a lifecycle,
but things are not quite the same as in the past. In particular, we no longer
have the same concept of a component-level Sub
Main that runs as a DLL is loaded, and the concept
of the Class_Terminate
event changes rather substantially. However, the concept behind the
Class_Initialize
event is morphed into a full-blown constructor method that accepts parameters.
Thus, in VB.NET, we are only guaranteed the following:
|
Event |
Description |
|
New |
Would run before any other code in our object; called by the runtime as the object was being created |
This is quite a change so let’s discuss the details further.
Construction
Object
construction is triggered any time we create a new instance of a class. This
is done using the New
keyword – a level of consistency that didn’t exist with VB6 where we got to
choose between New
and CreateObject.
Sub Main
Since
VB6 was based on COM, creating an object could trigger a Sub
Main procedure to be run. This would happen the first
time an object was created from a given component – often a DLL. Before even
attempting to create the object, the VB6 runtime would load the DLL and run
the Sub
Main procedure.
The .NET Common Language Runtime doesn’t
treat components quite the same way, and so neither does VB.NET. This means
that no Sub
Main procedure is called as a component is loaded.
In fact, Sub
Main is only used once – when an application itself
is first started. As further components are loaded by the application, only
code within the classes we invoke is called.
It wasn’t that wise to rely on Sub
Main even in VB6, since that code would run prior
to all the error handling infrastructure being in place. Bugs in Sub
Main were notoriously difficult to debug in VB6. If
we do have to use code that relies heavily on the Sub
Main concept for initialization, we’ll need to implement
a workaround in VB.NET.
This can be done easily by calling a central method from the constructor method in each class. For instance, we might create a centrally available method in a module such as:
Public
Module CentralCode
Private blnHasRun As Boolean
Public Sub Initialize()
If Not blnHasRun Then
blnHasRun = True
‘ Do initialization here
End If
End Sub
End
Module
This routine is designed to only run one time, no matter how often it is called. We can then use this method from within each constructor of our classes. For example:
Public
Class TheClass
Public Sub New()
CentralCode.Initialize()
‘ regular class code goes here
End Sub
End
Class
While this is a bit of extra work on our
part, it does accomplish the same effect we’re used to with a VB6-style Sub
Main routine.
New Method
Like the situation
with Sub
Main, Class_Initialize
is called before any other code in a VB6 class. Again, it is called before the
error handling mechanism is fully in place, making debugging very hard; errors
show up at the client as a generic failure to instantiate the object. Additionally,
Class_Initialize
accepts no parameters – meaning there is no way in VB6 to initialize an object
with data as it is created.
VB.NET eliminates Class_Initialize
in favor of full-blown constructor methods, which have full error handling capabilities
and do accept parameters. This means we can initialize our objects as we create
them – a very important and powerful feature. The constructor method in VB.NET
is SubNew.
The simplest constructor method for a class is one that accepts no parameters
– quite comparable to Class_Initialize:
Public Class TheClass
Public Sub New()
‘ initialize
object here
End Sub
End Class
With this type of constructor, creating an instance of our class is done as follows:
Dim obj As New TheClass()
This example is directly analogous to
creating a VB6 object with code in Class_Initialize.
However, more often than not we’d prefer to actually initialize our object with data as it is created. Perhaps we want to have the object load some data from a database, or perhaps we want to provide it with the data directly. Either way, we want to provide some data to the object as it is being created.
This is done by adding a parameter list
to the New
method:
Public Class TheClass
Public Sub New(ByVal
ID As Integer)
‘ use the
ID value to initialize the object
End Sub
End Class
Now, when we go to create an instance of the class, we can provide data to the object:
Dim obj As New TheClass(42)
To increase flexibility we might want
to optionally accept the parameter value. This can be done in two ways – through
the use of the Optional
keyword to declare an optional parameter, or by overloading
the New
method. To use the Optional
keyword, we simply declare the parameter as optional:
Public Sub New(Optional
ByVal ID As Integer = -1)
If ID = -1 Then
‘ initialize object here
Else
‘ use the ID value to initialize the object
End If
End
Sub
This approach is far from ideal, however, since we have to check to see if the parameter was or wasn’t provided, and then decide how to initialize the object. It would be clearer to just have two separate implementations of the New method – one for each type of behavior. This is accomplished through overloading:
Public Overloads Sub
New()
‘
initialize object here
End
Sub
Public Overloads Sub
New(ByVal ID As Integer)
‘ use the ID value to initialize the object
End
Sub
Not only does this approach avoid the
conditional check and simplify our code, but it also makes the use of our object
clearer to any client code. The overloaded New
method is shown by IntelliSense in the VS.NET IDE, making it clear that New
can be called both with and without a parameter.
In fact, through overloading we can create many different constructors if needed – allowing our object to be initialized in a number of different ways.
Constructor methods are optional in VB.NET. The only exception being when we’re using inheritance and the parent class has only constructors that require parameters. We’ll discuss inheritance later in the chapter.
Termination
In VB6 an object was destroyed when its last reference was removed. In other words, when no other code had any reference to an object, the object would be automatically destroyed – triggering a call to its Class_Terminate event. This approach was implemented through reference counting – keeping a count of how many clients had a reference to each object – and was a direct product of VB’s close relationship with COM.
While this behavior was nice – since we
always knew an object would be destroyed immediately and we could count on Class_Terminate
to know when – it had its problems. Most notably, it was quite easy to create
circular references between two objects, which could leave them running in memory
forever. This was one of the few (but quite common) ways to create a memory
leak in VB6.
To be fair, the problem was worse prior to VB6. In VB6, circular references are only a problem across components. Objects created from classes within the same component would be automatically destroyed in VB6, even if they had a circular reference. Still, the circular reference problem exists any time objects come from different components. The issue is non-trivial and has created a lot of headaches for VB developers over the years.
The clear termination scheme used in VB6 is an example of deterministic finalization. It was always very clear when an object would be terminated.
Unlike COM, the .NET runtime does not use reference counting to determine when an object should be terminated. Instead it uses a scheme known as garbage collection to terminate objects. This means that in VB.NET we do not have deterministic finalization, so it is not possible to predict exactly when an object will be destroyed. Let’s discuss garbage collection and the termination of VB.NET objects in more detail.
Garbage Collection
In .NET, reference counting is not part of the infrastructure. Instead, objects are destroyed through a garbage collection mechanism. At certain times (based on specific rules), a task will run through all of our objects looking for those that no longer have any references. Those objects are then terminated; the garbage collected.
This means that we can’t tell exactly when an object will really be finally destroyed. Just because we eliminate all references to an object doesn’t mean it will be terminated immediately. It will just hang out in memory until the garbage collection process gets around to locating and destroying it. This is an example of nondeterministic finalization.
The major benefit of garbage collection is that it eliminates the circular reference issues found with reference counting. If two objects have references to each other, and no other code has any references to either object, the garbage collector will discover and terminate them, whereas in COM these objects would have sat in memory forever.
There is also a potential performance benefit from garbage collection. Rather than expending the effort to destroy objects as they are dereferenced, with garbage collection this destruction process typically occurs when the application is otherwise idle – often decreasing the impact on the impact on the user. However, garbage collection may also occur with the application is active in the case that the system starts running low on resources.
We can manually trigger the garbage collection process through code:
System.GC.Collect()
This process takes time however, so it is not the sort of thing that should be done each time we want to terminate an object. It is far better to design our applications in such a way that it is acceptable for our objects to sit in memory for a time before they are finally terminated.
Finalize Method
The
garbage collection mechanism does provide some functionality comparable to the
VB6 Class_Terminate
event. As an object is being terminated, the garbage collection code will call
its Finalize
method – allowing us to take care of any final cleanup that might be required:
Protected
Overrides Sub Finalize()
‘ clean up code goes here
End
Sub
This code uses both the Protected
scope and Overrides
keyword – concepts we’ll discuss later as we cover inheritance. For now it is
sufficient to know that this method will be called just prior to the object
being terminated by the garbage collection mechanism – somewhat like Class_Terminate.
However, it is critical to remember that this method may be called long after the object is dereferenced by the last bit of client code (perhaps even minutes later).
Implementing a Dispose Method
In some cases the Finalize behavior is not acceptable. If we have an object that is using some expensive or limited resource – such as a database connection, a file handle, or a system lock – we might need to ensure that the resource is freed as soon as the object is no longer in use.
To accomplish this, we can implement a
method to be called by the client code to force our object to clean up and release
its resources. This is not a perfect solution, but it is workable. By convention,
this method is typically named Dispose:
Public
Sub Dispose()
‘ clean up code goes here
End
Sub
It is up to our client code to call this
method at the appropriate time to ensure cleanup occurs.
Again, the specific name of this method is up to us,
though within the .NET system class libraries the convention is to use the name
Dispose.
At this point we’ve largely covered the changes in behavior between VB6 and VB.NET in terms of creating classes and objects. Let’s move on and see how the substantial new inheritance feature works.
Related articles
Related discussion
-
How can I execute server-side function using asp.net Ontextchanged orJavascript onchange?
by mamoru0916 (0 replies)
-
Excel Oledb Engine and VB.NET
by anand.lv (1 replies)
-
we search the company in India for program creation under the order
by anand.lv (1 replies)
-
VB.NET Windows: Getting Windows Message from external class without overriding WndProc
by CallMeLaNN (0 replies)
-
String size limit and array upperbound limit
by Akhtar Hussain (2 replies)
Related jobs
-
Microsoft .Net Architect
in AMSTERDAM (€50K-€90K per annum)
Events coming up
-
Dec
6
Developing AJAX Web Applications with Castle Monorail
London, United Kingdom
Monorail is the model-view-controller engine of the Castle Project, bringing many of the best ideas of Ruby on Rails to the .NET world. In this talk, David De Florinier and Gojko Adzic show how Monorail makes it easy to develop .NET based AJAX applications, and how to use the Castle Project to build Web 2.0 applications effectively. Come to this session if you are a .NET web developer. Everyone is welcome!
application level
Error in the article "New Object-Oriented Capabilities in VB.NET - Events"
Derived classes cannot raise base class events in VB.Net. Handle them sure, but not raise them - even if they are declared public. In order to achieve this handle the base class event and raise a derived class event instead.
B.
While it is true that VB.NET cannot directly raise events in base classes from a derived class, there is an easy workaround. BTW, in C# you can simply call an event in a base class like: base.onMyEventName(EventArgs e).
But in VB.NET you cannot use MyBase.EventName() at all. But the workaround is an easy one.
1) In the base class add an overridable sub that simply raises an event defined in the base class.
Public MustInherit Class MyClass
Protected Event MyEventName(ByVal e as EventArgs)
Protected Overridable Sub OnMyEventName(ByVal e as EventArgs)
Raiseevent MyEventName(e)
End Sub
End Class
2) In the derived class make a call to the overridable method: Me.OnMyEventName(New EventArgs). It's really just that easy. Do it all the time.
Have Fun....
Error in the article "New Object-Oriented Capabilities in VB.NET - Events"
Derived classes cannot raise base class events in VB.Net. Handle them sure, but not raise them - even if they are declared public. In order to achieve this handle the base class event and raise a derived class event instead.
B.
How do we read the attributes in an XML file using a DataSet?
boig,
I needed the same thing and found that I have it working using the Public Shared declaration on the class that contains my XMLDocument and the functions to access the document. This makes the class available across the application.
Public Shared oTriggers As Triggers
If you only need the XMLDocument I would give that a try.
Hope this helps.
I need a global variable; but it is a "XmlDocument" object, does anybody know how do I have to create it, and how to initialitze it?
If you set a shared property of a user class in a ASP.NET application, how long wil the value last? Application level? Session level? Page level? Well i was actually looking for an answer to that when i got here... useful article anyway even though it doesn't answer my question...
This thread is for discussions of New Object-Oriented Capabilities in VB.NET.