Data Type Features
Visual Basic .NET has a number of features that simplify programming, including strong typing, type safety, and data widening, but the best new feature is probably the System.Object class, from which all other classes are derived.
The System.Object Class
Almost all modern programming languages have some
All of the common predefined types can be used from any language the .NET Framework supports. All of the System classes are contained in Mscorlib.dll and can be used by all .NET applications. You can use these classes as is or derive your own classes from them.
At the root of all classes (either inherited classes or those we write
All
|
Object.Method |
Access |
Description |
|
Equals |
Public |
Takes another object as a parameter and returns a Boolean True or False that indicates whether the two objects are equal. |
|
GetHashCode |
Public |
Returns an integer hash code that represents the object's value. This code is usually used as a key when the object is added to a collection. Two identical objects should generate the same code. |
|
Finalize |
Protected |
The CLR calls an object's
Finalize
method to notify the object that the object is about to be
|
|
MemberwiseClone |
Protected |
Creates a
|
|
GetType |
Public |
Returns an instance of System.Type , which is used to get information about the object through metadata. |
|
ToString |
Public |
Returns a string representation of the object. The string is not formatted and is almost always overridden by the class implementing it. |
Examine Figure 4-3, and notice that
Figure 4-3
The IntelliSense list displays Public methods only.
Now let's make use of those methods by extending the code shown in Figure 4-3 as
Imports System Public Module Module1 Dim myObject1 As New System.Object() Dim myObject2 As New System.Object() Dim bReslt As Boolean Public Sub main() myObject1 = "Hello Visual Basic .NET" myObject2 = 42 'What type is this? MessageBox.Show(myObject1.GetType.ToString) 'System.String 'What is the object's HashCode? MessageBox.Show(myObject1.GetHashCode.ToString) '-1757321832 'Are the objects equal? MessageBox.Show(myObject1.Equals(myObject2).ToString) 'False myObject1 = myObject2 MessageBox.Show(myObject1.Equals(myObject2).ToString) 'True MessageBox.Show(myObject1.ToString) '42 End Sub End Module
|
|
|
|
You must explicitly convert each message box argument to a string. This requirement is an example of the strong typing in Visual Basic .NET, which I'll cover in greater detail later in this chapter. |
|
|
|
The GetType method of myObject1 in the first message box prints System.Object . The GetHashCode method prints -1757321832 on my system, but your result might be different. In a real program, we would override the GetHashCode method in a derived class to return some unique number that could be used as, say, a key in a collection.
The next line of code sets myObject1 equal to myObject2 so that both objects now refer to myObject2. The last line prints out the value of myObject1 . This final message box indicates that both variables reference the same object—a memory location that holds the value 42.
As I mentioned earlier, the
Object
type
Because Visual Basic .NET uses strong typing, you should force early (compile-time) binding by dimensioning the variable as a specific object type. And if you can't dimension it as a specific type, you should cast (convert) it to a specific data type wherever possible.
Strong Typing
Strong typing requires you to specify a specific data type for each variable in your program. Strong-typed variables allow IntelliSense to display a variable's properties and methods as you work in the editor. Strong typing also
In previous versions of Visual Basic, the compiler provided the primitive data types. Recall from Chapter 1, "Visual Basic .NET from the Ground Up," that the primitive data types for all .NET languages are provided by the CLR. The .NET language compilers make a subset of all possible data types available to the various language
Type Safety
Visual Basic .NET is a type-safe language. You now know that you can access a variable only through the type associated with that variable—the compiler will bark if you do
Visual Basic 6 has the Option Explicit directive that forces all variables to be declared. Visual Basic .NET supports this directive and the new Option Strict directive, as shown in Table 4-4.
|
Directive |
Values |
Description |
|
Option Explicit |
On Off |
Used to force explicit declaration of all variables in a module. This directive is on by default. |
|
Option Strict |
On Off |
Restricts implicit data type conversions to widening conversions. This explicitly disallows any data type conversions in which data loss would occur. It also disallows any conversion between numeric types and strings. This directive is off by default. |
When the Option Strict directive is set to On, all variables must have an AS clause that explicitly declares the variable's type. Also, the & operator can't be used to concatenate Object variables. It's good practice to always use the Option Explicit directive in each module to ensure that each variable is explicitly declared using the Dim , the Private , the Public , or the ReDim statement—the compiler generates an error for each undeclared variable when Option Explicit is turned on. If Option Explicit and Option Strict are both turned off, you can use an undeclared variable—which would default to type Object— as shown here:
Option Explicit Off Option Strict Off myVariable = "Hello Visual Basic .NET"
Apart from the fact that undeclared variables can introduce subtle bugs into your programs, reading the code becomes a challenge because keeping track of undeclared variables is very difficult. Unless you have a really good reason—and I can't think of one off the top of my head—always keep both of these directives turned on. Think of them as free insurance policies.
Here's another example of what happens if you don't declare a data type. If Option Strict is off, the following code works fine.
Dim myVariable = "Hello Visual Basic .NET" MessageBox.Show(myVariable & " is type " & _ myVariable.GetType.ToString())
Notice that the Dim statement that declares myVariable does not include an AS clause, so myVariable defaults to type Object , which can hold any data type. This message box will display the contents of the variable as well as the data type, as shown in Figure 4-4.
Figure 4-4
In this figure, myVariable holds a string.
We could assign a double to the same object variable that just held a string.
myVariable = 123.456 MessageBox.Show(myVariable & " is type " & _ myVariable.GetType.ToString())
The message box shown in Figure 4-5 reveals that myVariable now holds a double.
Figure 4-5
In this figure, myVariable holds a double.
You might be scratching your head right about now, wondering why this code is legal if Visual Basic .NET is type safe. The answer is that it's not legal unless you
You can still generate the same type of bugs as you can in Visual Basic 6 by turning Option Explicit off. For example, you might declare oMyObject but later in the code use a variable named MyObject . Consider this code fragment:
Sub Main() Dim oMyObject As Object oMyObject = "Hello Visual Basic .NET!" MessageBox.Show(MyObject) End Sub
The programmer probably
Testing for Variable Type
As I've mentioned, Visual Basic .NET gives every data type a default value when the variable is dimensioned. Let's assume a program dimensions an Object variable and an Integer variable as shown here:
Dim myObject1 As New System.Object() Dim myInteger1 As New Integer() MessageBox.Show(myObject1.ToString) 'System.Object MessageBox.Show(myInteger1.ToString) '0
An Object variable's default implementation of the ToString method returns the fully qualified name of the object's class. Notice that the variable's value is not printed, as it is with the Integer variable. Although you'll usually override the ToString methods of your objects, the built-in methods are good for debugging purposes.
Value type variables inherit from the
Object.ValueType
class. The
Object.ValueType
class
We can test the type of object by using the built-in function TypeName , as shown here:
MessageBox.Show(TypeName(myObject1)) 'Object MessageBox.Show(TypeName(myInteger1)) 'Integer
A Typical Visual Basic .NET Assignment
Let's consider another example that clearly explains the type system. I know you'll soon run into the challenge of trying to figure out what data type the CLR is looking for in an assignment. For example, you might add a text box control named
txtName
to a form created from
Windows.Forms.Form
to display an employee's name. You might decide to change the text box background
txtName.BackColor = "yellow"
Unfortunately, the Visual Basic .NET editor
Figure 4-6
The Visual Basic .NET editor warns about data type
Take a moment and think about what you're trying to do. Of course, you're trying to set the BackColor property of the text box. You can see from the IDE message that the BackColor property is expecting a System.Drawing.Color data type, not a string. The Visual Basic .NET compiler can't explicitly coerce a string to a data type of System.Drawing.Color , nor would we want it to try.
Every data type in the .NET Framework exposes a set of properties and methods. You can solve the problem in this example by using these properties and methods to convert your string to the
System.Drawing.Color
data type that the
BackColor
property expects. The following code does the job
txtName.BackColor = System.Drawing.Color.Yellow
So you can see that the System.Drawing.Color class has various color constants, such as Yellow , that we can set. In addition to the color property, the System.Drawing.Color class has the useful method FromName . If you know the color constants that are predefined, you can use the FromName method and pass in a string with the color you want to display.
txtName.BackColor = System.Drawing.Color.FromName("blue")
These color assignment examples
Well, don't worry. In the next chapter, we'll examine the .NET Framework in detail and you'll learn how to quickly find what you need. And remember, to help us out the language designers provided the WinCV tool, which we saw in earlier chapters, that presents a hierarchical view that makes finding any class, method, or procedure a snap.
Data Widening
Earlier I mentioned that the Option Strict directive restricts implicit data type conversions to widening conversions. This restriction explicitly disallows any data type conversions in which data loss would occur and also disallows any conversion between numeric types and strings.
A widening conversion changes the value to a type that can accommodate data of the same or greater magnitude. Table 4-5 shows the standard data type widening conversions.
|
Data Type |
Widens to Data Types |
|
Byte |
Byte , Short , Integer , Long , Decimal , Single , Double |
|
Short |
Short , Integer , Long, Decimal, Single, Double |
|
Integer |
Integer, Long, Decimal, Single, Double |
|
Long |
Long, Decimal, Single, Double |
|
Decimal |
Decimal, Single, Double |
|
Single |
Single, Double |
|
Double |
Double |
|
Char |
String |
|
Any type |
Object |
Conversions from Integer to Single , or from Long or Decimal to Single or Double , might result in a loss of precision but never in a loss of magnitude. In this sense, they do not incur information loss because you are storing a value in a larger area. However, the reverse is not the case. Consider this code:
Dim iInteger As Integer = 10 Dim lLong As Long lLong = iInteger 'This works fine; we can safely convert ' an iInteger into a lLong. iInteger = lLong 'This does not work; we can't safely convert ' a lLong into an iInteger.
When this program is run, it generates an error message that states that Option Strict disallows implicit conversions from Long to Integer . Option Strict is again helping us stay away from subtle bugs. Of course, turning off Option Strict permits implicit conversions, but you should always stay away from doing this. It's easy to accidentally try to fit a number larger than can fit in an Integer into a variable named iInteger . And because this error happens at run time, you might never encounter the error until your program is deployed in the field. Option Strict ensures these overflow bugs can't happen in Visual Basic .NET
Any conversion that does not result in a loss of precision (a narrowing conversion) will not throw an exception. (An exception is a type of error. I'll cover exceptions in greater detail in Chapter 7, "Handling Errors and Debugging Programs.") You can still assign the contents of a Long variable to an Integer variable by explicitly casting the Long to an Integer . Of course, the compiler will assume you know what you are doing. Casting an expression means you are going to coerce an expression to a given type. In some programming circles, casting is known as "evil type coercion." Specific cast keywords are used to coerce expressions into the primitive types. The general cast keyword, CType , coerces an expression into any type. If no conversion exists from the type of the expression to the specified type, a compile-time error occurs. Otherwise, the result is the value produced by the conversion.
CastExpression ::= CType ( Expression , TypeName ) CastTarget ( Expression ) CastTarget ::= CBool CByte CChar CDate CDec CDbl CInt CLng CObj CShort CSng CStr
We could cast the contents of our Long into an Integer by doing the following:
iInteger = CType(lLong, Integer)
But here's the problem with doing this. Let's say the value of the Long is one more than the maximum value that can be held by an Integer . As our program runs, it calculates values and unexpectedly holds a value larger than we think. Then we try to cast the value of the Long into an Integer , as shown here:
Dim iInteger As Integer Dim lLong As Long = 2147483648 'One more than the maximum ' integer value (2,147,483,647) iInteger = CType(lLong, Integer)
Oops! We get a run-time error. You can't predict this error because a Long can always hold a larger value than an Integer . The only time this error will show up is during run time, as you can see in Figure 4-7.
Figure 4-7
There is no way to predict this error.
The
Integer
variable is not explicitly
A run-time error such as this is the worst type of error. A design-time error is easy to fix—in fact, we can't even compile and run the program until we fix it—but a run-time error is much more insidious. The erroneous code might be buried in a deep dark section of your program that rarely gets executed and might have been missed during testing. Or, depending on the sequence of events, a variable might be assigned a large value in an unforeseen way. So you see, the strong typing nature of Visual Basic .NET is really there for your benefit. It forces you to assign one data type to a like data type, or you have to explicitly cast it and accept responsibility for ensuring that no data loss occurs.
The moral of this story is to never shut off the Option Strict directive or the Option Explicit directive and be sure not to convert a large variable type into a smaller type unless you have an exceptional reason for doing so. And if you have an exceptional reason, you must be sure that the converted value will never exceed the storage capability of the smaller type.
It might seem surprising that a conversion from a derived (inherited) type to one of its base types is
In your programs, you can safely use widening conversions. Widening conversions will always succeed and can always be performed implicitly. However, a narrowing conversion can cause information loss. As we saw, narrowing conversions do not always succeed and might very well fail at run time. That's why the compiler does not allow implicit narrowing—you must explicitly perform this type of conversion.