by December 5, 2002 0 comments

System.Object is the super type of the CLR’s type system. The Object keyword in VB.NET maps to System.Object. Object is the type at the root of the type hierarchy. Any other type can be implicitly converted to the Object type. Object is somewhat similar to the VB 6.0 variant type. Previous versions of VB supported the Variant data type, which could be assigned to any primitive type (except fixed-length strings). In VB.NET, the functionality of the Variant and Object data types has been combined into one new data type: Object. The Object data type can be assigned to primitive data types, Empty, Nothing, Null and as a pointer to an object. When you upgrade a VB 6.0 project to VB.NET, all variables declared as Variant are changed to Object. For code inserted into the editor, the Variant keyword is replaced with Object.

Use early binding
Both VB 6 and VB.NET support late-binding objects, the practice of declaring a variable as the Object data type and assigning it to an instance of a class at runtime. But, during the upgrade process, late-bound objects can introduce problems when resolving default properties, or in cases where the underlying object model has changed and properties, methods and events need to be converted. 

Suppose you have a Form called Form1 with a label called Label1; the following Visual Basic 6 code would set the caption of the label to

Dim o As Object
Set o = Me.Label1
o.Caption = “SomeText”

In VB.NET Windows Forms, the Caption property of a label control is called Text. When your code is upgraded, all instances of the Caption property are changed to Text, but because a late-bound object is type-less, VB cannot detect what type of object it is, or if any properties should be translated. In such cases, you will need to change the code yourself after upgrading. If you rewrite the code using early-bound objects, it will be upgraded automatically:

Dim o As Label1
Set o = Me.Label1
o.Caption = “SomeText”

You should declare variables of the appropriate object type rather than declaring them as Object or Variant data type. In some cases, where you do use Object or Variant variables in your VB 6.0 code, we recommend you use explicit conversions when you assign the variables, perform operations on the variables or pass the variables to a function. For eg, the intention of the ‘+’ operator in the following code is unclear. Should Var1 and Var2 be added as strings or integers?

Dim Var1 As Variant
Dim Var2 As Variant
Dim Var3 As Variant
Var1 = “3”
Var2 = 4
Var3 = Var1 + Var2

The above example may result in a runtime error in VB.NET. Rewriting the final line to use explicit conversions ensures the code will work

Var3 = CInt(Var1) + CInt(Var2) 

If you declare a variable without assigning any type to it, it is by default of type Object, meaning they all finally derive from System.Object. Same for UDC (User Defined Classes). Instances of all types are assignable to variables of type Object as seen in Listing 1. Parameters of type Object are flexible, yet not type-safe. Refer to Listing 2 to see how this works.

Casting between types
It is quite common to cast an object from one data type to another. Now, we’ll look at the rules that govern how objects are cast between data types. Make sure “Option Strict On” appears at the top of the code

Dim obj As System.Object = New Foo()

Compiles and executes correctly because there is an implied cast. The new operator returns a reference to a Foo type, but o is a reference to a System.Object type. Since all types (including the Foo type) can be cast to System.Object, the implied cast is successful. However, if you execute

Dim fooObj As Foo = obj

you get a compiler error since the compiler does not provide an implicit cast from a base type to a derived type. To get the command to compile, you must insert an explicit cast as follows

Dim fooObj As Foo = CType(obj, Foo)

Let’s look at another example 

Dim obj As System.Object= New System.Object()
Dim fooObj As Foo = CType(obj, Foo)

In the first line, I have created an object of type System.Object. In the second line, I am attempting to convert a reference of type System.Object to a reference of type Foo. Thecode compiles fine. However, when executed, the second line generates an InvalidCastException exception, which if not caught, forces the application to terminate. When the second line executes, the CLR verifies that the object referred to by obj is in fact an object of type Foo (or any type derived from type Foo). If so, the CLR allows the cast. But, if the object referenced by obj has no relationship to Foo, or is a base class of Foo, then theCLR prevents the unsafe cast and raises the InvalidCastException exception.

When using VB.NET, knowing when and how to cast between types is essential. VB.NET allows implicit casting between convertible types. Implicitly casting non-convertible types is not permitted when Option Strict is On. It also doesn’t permit implicit conversion when there is potential loss of data/precision. Explicit casting can be performed using CType, CInt, CStr etc. Listing 3 shows how narrow conversions are prohibited. As you can see in Listing 4, explicit casts are required when converting from System.Object. Narrowing conversions can result in loss of data. So, check that the actual value will fit in an Integer before making the conversion. Casting can be performed on both value and reference types. Widening conversions, going from a smaller data type to a larger one will be performed implicitly by the compiler. Other conversions must be done explicitly.

Shared vs Instance members
Regular members of a class, referred to as Instance members are per object based. Each instance of a class will have its own copy of the members. But at times, there is a necessity to have a member, which is shared across instances. For eg, you might want to keep a count of the number of instances for a particular class. You can’t do this with instance members. This is where VB.NET introduces Shared members. VB.NET is fully an object-oriented language, and it also provides a feature wherein you can have a member that has a class-wide scope, not instance-wide.

Shared members must be defined using the Shared keyword (Listing 5). A shared method is not accessed via an object instance like a regular method, but directly from the class. Very good examples of Shared members are the Math and Console classes of the .NET framework. You can say Math.Sqrt(255) or Console.WriteLine(“Hi there!”) where Sqrt and WriteLine are Shared members of the Math and Console classes respectively. We can use these methods without instantiating a Math or Console object.

A class can have both instance members and shared members. Const fields are implicitly Shared. Shared methods can also be accessed via other objects just like regular methods, but their most common use is to provide functionality without the requirement for creating an object. When a shared method is invoked, no object is created — the method is called directly, much like a procedure in a Module.

at Class scope
at Object scope. 
accessible through a class definition.
accessible through objects. 
only access other shared members.
access other instance members and shared members.
for Shared fields is allocated on a per-class basis.
for Instance fields is allocated on
a per-object basis.

Shared methods can also be overloaded just like regular methods, so it is quite possible to create a set of variations on the same shared method, each having a different parameter list. A class can also have a Shared constructor that executes once per class. You can see one such example in Listing 6. The default scope for a shared method is Public. It is possible to restrict the scope of a shared method to Friend, Protected or Private by prefixing the declaration with the appropriate scope. When overloading a method we can have different scopes on each implementation, as long as the parameter lists are different.
As with shared methods, we can scope the shared variables as required. Where shared methods are Public by default, shared variables are Private by default. In general, it is good practice to always explicitly define the scope of methods and variables to avoid confusion. The important thing about shared variables is that they are common across all instances of the class. Listing 7 has an example of effectively shared variables can be used in an application to keep a count on the number of instances of a class. Based on this class, as we create each instance of the MyCounter class, the counter is incremented by one.

At any time, we can retrieve the count value via the Count property. Thus, if we run the client code in Listing 8, we’ll get a value if 3. As long as our application is running, the counter will remain valid. Once our application terminates, the counter also goes away.

This technique can be very useful for server processes that run “forever” since they can keep usage counters or other values over time very easily. The values are only reset when the process is restarted.

Ajay J Singala, Synergetics

No Comments so far

Jump into a conversation

No Comments Yet!

You can be the one to start a conversation.