New Object-Oriented Capabilities in VB.NET

Interacting with Objects

With all the changes to the way we declare, construct, and implement classes, it makes sense that there are also some changes in the way we interact with objects. These changes impact on how we instantiate objects, reference and dereference objects, and how we use early and late binding techniques.

Object Declaration and Instantiation

The most obvious change in the way we work with objects comes as we try to create them and work with our object references. VB.NET doesn’t use the CreateObject statement for object creation. CreateObject was an outgrowth of VB’s relationship with COM, and since VB.NET doesn’t use COM, it has no use for CreateObject.

Technically VB.NET can use COM objects through an interoperability mechanism. This is discussed in Chapter 9. However, in typical .NET programming, COM doesn’t enter the picture.

New Statement

VB.NET relies on the New statement for all object creation. We can use New in a number of different locations within our code – all of them perfectly valid.

The most obvious is to declare an object variable and then create an instance of the object in an instance of the class:

Dim obj As TheClass
obj = New TheClass()

We can shorten this by combining the declaration of the variable with the creation of the instance:

Dim obj As New TheClass()

In VB6 this was a very poor thing to do, as it had both negative performance and maintainability effects. However, in VB.NET there is no difference between our first example and this one, other than that our code is shorter.

Keep in mind that the scope of our variable comes into play here. If we declare a variable within a block structure, that variable will only be valid within that block structure. In many cases we’ll want to declare the variable within the scope of our method, but possibly create instances of the object within a block structure such as a Try...End Try or loop structure. In such a case, combining the declaration with the instantiation may be inappropriate.

Another variation on the declaration and instantiation theme is:

Dim obj As TheClass = New TheClass()

Again, this both declares a variable and creates an instance of the class for our use. This syntax is perhaps more useful when working with inheritance or with multiple interfaces. We might declare the variable to be of one type – say an interface – and instantiate the object based on a class that implements that interface:

Dim obj As MyInterface = New TheClass()

We can employ more complex syntax at times also. Suppose that we have a method that requires an object reference. We can create an instance of the object right in the call to the method:

DoSomething(New TheClass())

This calls the DoSomething method, passing a new instance of TheClass as a parameter. This new object will only exist for the duration of this one method call. When the method completes, the object will be automatically dereferenced by the .NET runtime.

Remember that dereferencing an object doesn’t mean it is immediately destroyed. As we discussed earlier, objects are only destroyed when the .NET garbage collection process runs through and cleans up orphaned objects.

This can be even more complex. Perhaps, instead of needing an object reference, our method needs a String. We can provide that String value from a method on our object – instantiating the object and calling the method all in one shot:

DoSomething(New TheClass().GetStringData())

Obviously we need to carefully weigh the readability of such code against its compactness – at some point having more compact code can detract from readability rather than enhancing it.

No Set Keyword

Notice that nowhere do we use the Set statement when working with objects. In VB6, any time we worked with an object reference we had to use the Set command – differentiating objects from any other data type in the language.

In VB.NET, objects are not treated differently from any other data type, and so we can use direct assignment for objects just like we do with Integer or String data types. The Set command is no longer valid in VB.NET. See Chapter 3 for more details.

Dereferencing Objects

In VB6, we’d dereference an object by setting our object reference to Nothing. The same is true in VB.NET:

Dim obj As TheClass
obj = New TheClass()
obj = Nothing

The effect of this statement is different in VB.NET, however. As we discussed earlier, VB.NET does not use reference counting to terminate objects, instead relying on a garbage collection mechanism. In VB6, when no more variables held a reference to an object, that object was immediately destroyed. In VB.NET this is not true – the object will be destroyed when the garbage collection process discovers that the object has no references. That is something that may happen seconds or even minutes after the last reference is removed.

This doesn’t eliminate the value of dereferencing objects however. If we have a long-running algorithm, it is a good practice to explicitly dereference objects within the process – thus allowing the garbage collector to remove them when possible. As long as our code retains a reference to an object, that object will remain in memory and will not be garbage collected.

 

 

Early vs. Late Binding

One of the strengths of VB has long been that we had access to both early and late binding when interacting with objects.

Early binding means that our code directly interacts with the object – knowing its data type ahead of time and thus being able to very efficiently interact with the object. Early binding allows the IDE to use IntelliSense to aid our development efforts and it allows the compiler to ensure that we are referencing methods that do exist and that we are providing the proper parameter values.

Late binding means that our code interacts with an object dynamically at run-time. This provides a great deal of flexibility since our code literally doesn’t care what type of object it is interacting with as long as the object supports the methods we want to call. Because the type of the object isn’t known by the IDE or compiler, neither IntelliSense nor compile-time syntax checking is possible – but in exchange we get unprecedented flexibility.

VB.NET continues this tradition, providing support for both early and late binding as we work with
our objects.

By default, all objects are early bound. The IDE and compiler enforce this as long as Option Strict On is set, and this is the default. However, if we set Option Strict Off at the top of a source file (as discussed in Chapter 3), we open the door for late binding throughout the code in that file.

Use of the Object Type

Late binding occurs when the compiler can’t determine the type of object we’ll be calling. This level of ambiguity is achieved through the use of the Object data type. A variable of data type Object can hold virtually any value – including a reference to any type of object. Thus, code such as the following could be run against any object that implements a MyMethod method that accepts no parameters:

Option Strict Off
Module LateBind
  Public Sub DoSomething(obj As Object)
    obj.MyMethod()
  End Sub
End Module

If the object passed into this routine doesn’t have a MyMethod method that accepts no parameters, then a run-time error will result. Thus, it is recommended that any code that uses late binding you should always provide error trapping:

Option Strict Off
Module LateBind
  Public Sub DoSomething(obj As Object)
    Try
      obj.MyMethod()
    Catch
      ‘ do something appropriate given failure to call the method
    End Try
  End Sub
End Module

While late binding is flexible, it can be error prone and it is slower than early bound code. To make a late bound method call, the .NET runtime must dynamically determine if the target object actually has a method that matches the one we’re calling, and then it must invoke that method on our behalf. This takes more time and effort than an early bound call where the compiler knows ahead of time that the method exists and can compile our code to make the call directly.

Late Binding and Reflection

The .NET Framework supports a concept known as reflection. This is the ability to write code that examines other .NET code to determine its composition. Reflection is supported by the System.Reflection namespace.

Reflection allows us to write code that discovers the classes within an assembly and the methods, properties and events exposed by those classes. We can then use reflection to create instances of those classes and call those methods. This entire process can be very dynamic – much like late binding.

In fact, VB.NET uses reflection to implement late binding on our behalf. Rather than forcing us to write the code that uses reflection to find and invoke a method, VB.NET handles this for us when we use late binding coding techniques.

We could implement a limited form of reflection within VB6 by using the typelib DLL. The functions in this DLL allowed us to dynamically discover the classes and methods in a COM DLL, and then invoke them. Of course COM components were described with IDL – a rather inaccurate description of the component. In .NET, assemblies are described by metadata that accurately describes each assembly, making reflection a much more robust solution.

Use of the CType Function

Whether we are using late binding or not, it can be useful to pass object references around using the Object data type – converting them to an appropriate type when we need to interact with them. This is done using the CType function, allowing us to use a variable of type Object to make an early bound method call:

Module LateBind
  Public Sub DoSomething(obj As Object)
    CType(obj, TheClass).MyMethod()
  End Sub
End Module

Even though the variable we’re working with is of type Object – and thus any calls to it will be late bound – we are using the CType method to temporarily convert the variable into a specific type, in this case the type TheClass.

This technique is often called casting. If we think of each interface or class type as a mold, we can cast an object of one type into the mold of another class or interface.

The CType function can be very useful when working with objects that implement multiple interfaces, since we can reference a single object variable through the appropriate type as needed. For instance, if we have an object of type TheClass that also implements MyInterface, we can use that interface with the following code:

Dim obj As TheClass
obj = New TheClass
CType(obj, MyInterface).DoSomething()

In this way we can make early bound calls to other interfaces on an object without needing to declare a new variable of the interface type as we had to do in VB6.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“In theory, theory and practice are the same. In practice, they're not.”