Search   Articles   Dev Forums   Personalize   Favorites   Member Login   ASP.Net Hosting
DevASP.NET for ASP.NET, VB.NET, XML and C# (C-Sharp) Developers Wednesday, January 07, 2009

Dev Articles
Search Directory
ASP.NET
VB.Net
C-Sharp
SQL Server
 

Working with Objects


In the .NET environment, and within VB in particular, we use objects all the time without even thinking about it. Every control on a form – and, in fact, every form – is an object. When we open a file or interact with a database we are using objects to do that work.

Object Declaration and Instantiation

Objects are created using the New keyword – indicating that we want a new instance of a particular class. There are a number of variations on how or where we can use the New keyword in our code. Each one provides different advantages in terms of code readability or flexibility.

 

Unlike previous versions of VB, 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. The CreateObject method still exists to support COM interoperability, but is not used to access .NET objects.

 

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

 

Dim obj As TheClass

obj = New TheClass()

 

The result of this code is that we have a new instance of TheClass ready for our use. To interact with this new object, we will use the obj variable that we declared. The obj variable contains a reference to the object – a concept we'll explore more later.

 

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

 

Dim obj As New TheClass()

 

In previous versions of VB 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.

 

This code both declares the variable obj as data type TheClass and also creates an instance of the class – immediately creating an object that we can use from our code.

 

Another variation on this theme is:

 

Dim obj As TheClass = New TheClass()

 

Again, this both declares a variable of data type TheClass and creates an instance of the class for our use.

 

This third syntax provides a great deal of flexibility while remaining compact. Though it is a single line of code, it separates the declaration of the variable's data type from the creation of the object.


Such flexibility is very 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. We'll cover interfaces in detail in Chapter 6 but as an example here, let's create an interface named ITheInterface:

 

Public Interface ITheInterface

Sub DoSomething()

End Interface

 

Our class can then implement that interface, meaning that our class now has its own native interface and also has a secondary interface – ITheInterface:

 

Public Class TheClass

Implements ITheInterface

 

Public Sub DoSomething() Implements ITheInterface.DoSomething

' implementation goes here

End Sub

End Class

 

We can now create an instance of TheClass, but reference it via the secondary interface by declaring the variable to be of type ITheInterface:

 

Dim obj As ITheInterface = New TheClass()

 

We can also do this using two separate lines of code:

 

Dim obj As ITheInterface

obj = New TheClass()

 

Either technique works fine and achieves the same result, which is that we have a new object of type TheClass, being accessed via its secondary interface. We'll discuss multiple interfaces in more detail in Chapter 6.

 

So far we've been declaring a variable for our new objects. However, sometimes we may simply need to pass an object as a parameter to a method – in which case we can create an instance of the object right in the call to that method:

 

DoSomething(New TheClass())

 

This calls the DoSomething method, passing a new instance of TheClass as a parameter.

 

This can be even more complex. Perhaps, instead of needing an object reference, our method needs an Integer. We can provide that Integer value from a method on our object:

 

Public Class TheClass

Public Function GetValue() As Integer

Return 42

End Function

End Class


We can then instantiate the object and call the method all in one shot, thus passing the value returned from the method as a parameter:

 

DoSomething(New TheClass().GetValue())

 

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.

 

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.

Object References

Typically, when we work with an object we are using a reference to that object. On the other hand, when we are working with simple data types such as Integer, we are working with the actual value rather than a reference. Let's explore these concepts and see how they work and interact.

 

When we create a new object using the New keyword, we store a reference to that object in a variable. For instance:

 

Dim obj As New TheClass()

 

This code creates a new instance of TheClass. We gain access to this new object via the obj variable. This variable holds a reference to the object. We might then do something like this:

 

Dim another As TheClass

another = obj

 

Now we have a second variable, another, which also has a reference to that same object. We can use either variable interchangeably, since they both reference the exact same object. The thing we need to remember is that the variable we have is not the object itself but, rather, is just a reference or pointer to the object itself.

Dereferencing Objects

When we are done working with an object, we can indicate that we're through with it by dereferencing the object.

 

To dereference an object, we need to simply set our object reference to Nothing:


 

Dim obj As TheClass

 

obj = New TheClass()

obj = Nothing

 

This code has no impact on our object itself. In fact, the object may remain blissfully unaware that it has been dereferenced for some time.

 

Once any and all variables that reference an object are set to Nothing, the .NET runtime can tell that we no longer need that object. At some point, the runtime will destroy the object and reclaim the memory and resources consumed by the object.

 

Between the time that we dereference the object and the time that .NET gets around to actually destroying it, the object simply sits in memory – unaware that it has been dereferenced. Right before .NET does destroy the object, the framework will call the Finalize method on the object (if it has one). We discussed the Finalize method in Chapter 3.

Early versus Late Binding

One of the strengths of Visual Basic 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, by directly calling its methods. Since the VB compiler knows the object's data type ahead of time, it can directly compile code to invoke the methods on the object. Early binding also allows the IDE to use IntelliSense to aid our development efforts; it allows the compiler to ensure that we are referencing methods that do exist and that we are providing the proper parameter values.

 

In previous versions of VB, early binding was also known as vtable binding. The vtable was an artifact of COM, providing a list of the addresses for all the methods on an object's interface. In .NET, things are simpler and there is no real vtable. Instead, the compiler is able to generate code to directly invoke the methods on an object. From a VB coding perspective this makes no difference, but it is quite a change behind the scenes.

 

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 we get unprecedented flexibility in exchange.

 

If we enable strict type checking by using OptionStrictOn at the top of our code modules, then the IDE and compiler will enforce early binding behavior. By default, OptionStrict is turned off and so we have easy access to the use of late binding within our code. We discussed OptionStrict in Chapter 4.

Implementing Late Binding

Late binding occurs when the compiler can't determine the type of object that 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 DoSomething method that accepts no parameters:


 

Option Strict Off

 

Module LateBind

Public Sub DoWork(ByVal obj As Object)

obj.DoSomething()

End Sub

End Module

 

If the object passed into this routine does not have a DoSomething method that accepts no parameters, then a run-time error will result. Thus, it is recommended that any code that uses late binding always provides error trapping:

 

Option Strict Off

 

Module LateBind

Public Sub DoWork(ByVal obj As Object)

Try

obj.DoSomething()

Catch ex As Exception When Err.Number = 438

' do something appropriate given failure to call the method

End Try

End Sub

End Module

 

Here, we've put the call to the DoSomething method in a Try block. If it works then the code in the Catch block is ignored but, in the case of a failure, the code in the Catch block is run. We would need to write code in the Catch block to handle the case that the object did not support the DoSomething method call. This Catch block, in fact, only catches error number 438, which is the error indicating that the method doesn't exist on the object.

 

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. With a late bound call, the compiler has to generate code to make the call dynamically at runtime.

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 particularly useful when working with objects that use inheritance or implement multiple interfaces –concepts that we'll discuss in Chapter 6.

 

If Option Strict is turned off, which is the default, we can write code that allows us to use a variable of type Object to make an early bound method call:

 

Module LateBind

Public Sub DoWork(obj As Object)

Dim local As TheClass


 

local = obj

local.DoSomething()

End Sub

End Module

 

We are using a strongly typed variable, local, to reference what was a generic object value. Behind the scenes, VB.NET converts the generic type to a specific type so it can be assigned to the strongly typed variable. If the conversion can't be done we'll get a trappable runtime error.

 

The same thing can be done using the CType function. If Option Strict is enabled, then the previous approach will not compile and the CType function must be used. Here is the same code making use of CType:

 

Module LateBind

Public Sub DoWork(obj As Object)

Dim local As TheClass

 

local = CType(obj, TheClass)

local.DoSomething()

End Sub

End Module

 

Here, we've declared a variable of type TheClass, which is an early bound data type that we want to use. The parameter we're accepting, though, is of the generic Object data type, and so we use the CType() method to gain an early bound reference to the object. If the object isn't of type TheClass, the call to CType() will fail with a trappable error.

 

Once we have a reference to the object, we can call methods by using the early bound variable, local.

 

Since all the method calls with CType() are early bound, this code will work even if we override the default and set Option Strict On.

 

This code can be shortened to avoid the use of the intermediate variable. Instead, we can simply call methods directly from the data type:

 

Module LateBind

Public Sub DoWork(obj As Object)

CType(obj, TheClass).DoSomething()

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.

If the object passed as a parameter is not of type TheClass, we will get a trappable error, so it is always wise to wrap this code in a Try…Catch block.


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, as we discussed earlier, if we have an object of type TheClass that also implements ITheInterface, we can use that interface with the following code:

 

Dim obj As TheClass

 

obj = New TheClass

CType(obj, ITheInterface).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. We'll discuss multiple interfaces in detail in Chapter 6.




DevASP.Net - Disclaimer - Privacy
Copyright © 2008 DevASP.net