Types and Type Operations

Types and Type Operations

Visual Basic .NET offers most of the same types as Visual Basic 6. You will find the same basic types, such as Byte, Integer, Single, and String. You can also define the same types, such as Enum, user-defined types (called Structure in Visual Basic .NET), and Class. Visual Basic .NET also offers some new types that you can use Short, Char, and Decimal. It also gives you a new Interface type that you can define or use. This section focuses on Visual Basic 6 types for which support has changed in Visual Basic .NET.

Object Replaces Variant

The Variant data type has existed in the Visual Basic language since Visual Basic 2. It is part of the Visual Basic franchise. If you do not specify a type for a variable, you can always rely on Variant being the default type. The beauty of the Variant data type is that you can assign any numeric, string, array, or object reference value to it. It can also be in various states of nothingness missing, Null, or Nothing.

The same characteristic that makes the Variant data type easy to use is also the one that will get you into trouble. The compiler will not and cannot step in to save you if you assign variants containing incompatible types to each other. For example, if you attempt to assign a variant containing an object to a variant containing a simple type such as an Integer, you will encounter a run-time error.

Visual Basic .NET introduces the Object type. It is a combination of the Object and Variant types found in Visual Basic 6. Like the Visual Basic 6 Object type, a Visual Basic .NET object can be assigned to an object instance, such as a control, and you can make late-bound calls to the object. The Variant-like behavior of the Object type is that you can assign any type to it, ranging from Integer, Array, and Structure to class objects.

IsMissing Is Lost

One characteristic that the Visual Basic .NET Object type lacks compared to the Visual Basic 6 Variant type is a value that means missing. Other than being set to an actual value, the only other state a Visual Basic .NET Object type can be set to is Nothing. The following Visual Basic 6 code demonstrates how this can be a problem if you are testing for a missing optional parameter:

Private m_Info As String Private m_Picture As StdPicture Public Sub SetData(Optional ByVal Info As String, _                    Optional ByVal Picture As Variant)    If Not IsMissing(Info) Then       m_Info = Info    End If         If Not IsMissing(Picture) Then       Set m_Picture = Picture    End If      End Sub

Because a Visual Basic .NET object cannot be set to a value representing missing, the IsMissing function is not available in Visual Basic .NET. The closest approximation is IsNothing. The Upgrade Wizard will replace all of your calls to IsMissing with calls to IsNothing.

Visual Basic .NET requires that you specify a default value for all optional parameters to be used if no argument is passed in. The wizard will automatically initialize parameters to reasonable initial values depending on the type; a string is set to an empty string, a numeric is set to 0, and an object parameter is set to Nothing.

Let s suppose that in the code just given you wanted to pass in Nothing as the value for the Picture parameter. Your intent is to erase any current picture. Inspect the following upgraded code, and you will see that the logic cannot distinguish between a missing parameter and a parameter for which you explicitly pass in Nothing. The Picture parameter will always be set to Nothing in either case. If you explicitly pass in Nothing to erase the current picture, the Not IsNothing(Picture) test will fail and the picture member (m_Picture) will not be set to Nothing; in other words, the picture will not be erased.

Private m_Info As String Private m_Picture As System.Drawing.Image Public Sub SetData(Optional ByVal Info As String = "", _                    Optional ByVal Picture As Object = Nothing)           'UPGRADE_NOTE: IsMissing() was changed to IsNothing().    If Not IsNothing(Info) Then       m_Info = Info    End If           'UPGRADE_NOTE: IsMissing() was changed to IsNothing().    If Not IsNothing(Picture) Then       m_Picture = Picture    End If    End Sub

How should you modify your code to cope with this situation? You need to reintroduce the concept of IsMissing into your code. One approach is to set the initialization value to a unique value that will not be passed in under normal circumstances. For example, in the previous code, you could set the parameter initialization values to <Missing> . If you see that a parameter value is set to <Missing> , you can assume that the caller did not specify the argument when making the call. The logic will be compatible with the way it was before. If the values are not specified, the current value is left intact, and if the caller passes in a value including Nothing or an empty string the current value will be replaced by the passed-in value. Applying <Missing> to the parameter initialization values and fixing up the code to test for <Missing> yields the following:

Private m_Info As String Private m_Picture As System.Drawing.Image Public Sub SetData(Optional ByVal Info As String = "<Missing>", _                    Optional ByVal Picture As Object = "<Missing>")       If Info <> "<Missing>" Then       m_Info = Info    End If    If Picture <> "<Missing>" Then       m_Picture = Picture    End If End Sub

Dealing with Null

As we discussed in the previous section, an Object can be in only one of two states set to a value or to Nothing. A Visual Basic Variant, on the other hand, can contain other states, such as empty, Null, or missing. We can work around the absence of the missing state by using a special value that you create to mean missing. When dealing with Null, you do not need to create your own value. Rather, the .NET Framework provides the System.DBNull.Value object as the equivalent to a Visual Basic 6 Null value.

Why is the name of the value DBNull.Value? Why not just Null? The reason is that the DB in DBNull is short for database. Since it is most common to deal with null values when reading values from or writing values to database fields, DBNull.Value is intended to be used when you want to set or check for null values in a database table. When you are setting an object variable to a field contained in an ADO.NET dataset or to a field in a traditional ADO recordset, if the field value is Null, the object variable will be set to System.DBNull.Value by convention.

Null Propagation

Many Visual Basic 6 functions such as Left, Mid, Chr, and Hex deal with Variant values containing Null. These functions return a Variant containing Null. Returning a Null value in response to a passed-in Null value is known as null propagation. The null-propagating functions all accept Variant values and return Variant values. A set of sibling functions in the Visual Basic 6 language accepts strings and returns strings. These functions such as Left$, Mid$, Chr$, and Hex$ will result in an error if you attempt to pass in a value of Null.

By supporting null propagation, Visual Basic 6 allows you to write working code that does not need to check for Null. For example, if you have code that converts a customer name taken from a field in a database to lowercase, the code will work even if the customer name is not provided and is Null. The following Visual Basic 6 code will execute without error even though vCustomerMiddleName is Null.

Dim vCustomerMiddleName As Variant vCustomerMiddleName = Null vCustomerMiddleName = LCase(vCustomerMiddleName)

Visual Basic .NET does not support null propagation. This means that although the same Visual Basic 6 functions Left, Mid, Chr, Hex, and LCase are available in Visual Basic .NET, the Visual Basic .NET functions do not accept Null System.DBNull.Value to be exact as a legal value. The Upgrade Wizard produces the following code from the previous Visual Basic 6 example:

Dim vCustomerMiddleName As Object vCustomerMiddleName = System.DBNull.Value vCustomerMiddleName = LCase(vCustomerMiddleName)

This code leads to a run-time exception: No accessible overloaded Strings.LCase can be called without a narrowing conversion. This message is an overly technical, wordy way of saying that the LCase function does not accept System.DBNull.Value as a legal argument.

How can you fix this problem? The obvious fix is to insert a check before the call to every Visual Basic .NET function to make sure that the passed-in argument is not System.DBNull.Value. We could modify the code as follows:

Dim vCustomerMiddleName As Object vCustomerMiddleName = System.DBNull.Value If Not vCustomerMiddleName Is System.DBNull.Value Then    vCustomerMiddleName = LCase(vCustomerMiddleName) End If

This fix will work great if you are making a couple of calls to LCase in your code, but what if you are making hundreds or thousands of calls to the function? Adding checks for System.DBNull.Value before all calls to your Visual Basic .NET functions will be too time-consuming. Another solution is to declare your own LCase function that handles null propagation. For example, you can create the LCase function in a module where it can be accessed from anywhere in your code as follows:

Public Function LCase(ByVal obj As Object) As Object    If Not obj Is System.DBNull.Value Then       ' Value is not null so call Visual Basic .NET function       obj = Microsoft.VisualBasic.LCase(obj)    End If    Return obj End Function

If you place this function in a module named Module1, it is a simple matter to search for and replace all your calls to LCase with ones to Module1.LCase.

Arrays

Visual Basic .NET supports two array types strongly typed and generic System.Array. Strongly typed arrays are the arrays you are accustomed to using in Visual Basic 6. Anytime you use the Dim statement to declare an array variable of a specific type, you are creating a strongly typed array. Visual Basic .NET actually the .NET Framework offers a new System.Array class. This class offers a set of methods so that you can define an array of any type and any number of dimensions at run time. An important difference between the two array types is that strongly typed arrays do not support nonzero lower bounds, whereas the System.Array class does.

Because Visual Basic .NET offers primary support for strongly typed arrays, it is not possible to declare a strongly typed array with a nonzero lower bound. Visual Basic .NET in turn does not support nonzero-bound array-related features such as array declarations including the To statement and Option Base 1. It does support LBound, but when LBound is used on a strongly typed array, it will always return 0.

The Upgrade Wizard will upgrade your arrays to strongly typed Visual Basic .NET arrays. If you are using Option Base 1, the wizard will issue an UPGRADE_WARNING message: Lower bound of array <ArrayName> was changed from 1 to 0. The UPGRADE_WARNING is added for each array declaration found that does not specify a lower bound. If you are using a positive value for the array s lower bound, the wizard will remove the lower bound from the declaration and issue the same UPGRADE_WARNING. If you specify a negative value for the lower bound in your array declaration, the wizard will leave the declaration as is and will include an UPGRADE_ISSUE comment: Declaration type not supported: Array with lower bound less than zero. The following code samples demonstrate how the wizard upgrades your Visual Basic 6 array declarations. The Visual Basic 6 code

Option Base 1 Dim OptionOneBasedArray(10) As Integer Dim PositiveLBoundArray(10 To 20) As Long Dim NegativeLBoundArray(-10 To 10) As Variant

upgrades to the following Visual Basic .NET code:

'UPGRADE_WARNING: Lower bound of array OptionOneBasedArray  'was changed from 1 to 0.  Dim OptionOneBasedArray(10) As Short 'UPGRADE_WARNING: Lower bound of array PositiveLBoundArray was  'changed from 10 to 0. Dim PositiveLBoundArray(20) As Integer 'UPGRADE_ISSUE: Declaration type not supported: Array with  'lower bound less than zero. Dim NegativeLBoundArray(-10 To 10) As Object   

If you are using positive lower bounds in your array declaration, the upgraded declaration will compile and run. However, if your code uses hard-coded references to the lower bound of the array, the array will have more elements than you need or will use. For example, suppose that you had code that accessed each element of the PositiveLBoundArray, as follows:

Dim i As Integer For i = 10 To 20    PositiveLBoundArray(i) = i Next

This code will work without any problem. The only issue is that there are now 10 elements of the array, 0 through 9, that are not being used.

If, in Visual Basic 6, you declared your array with a negative lower bound, the wizard will leave the declaration as is. The declaration will result in a compiler error in Visual Basic .NET, however. You will need to change your array to use a 0 lower bound, and you may need to adjust your code to work with the new array bound. For example, you could change the NegativeLBoundArray declaration to use a 0 bound and the same number of elements as follows:

Dim NegativeLBoundArray(20) As Object

You would then need to update your code that accesses the elements of NegativeLBoundArray. Suppose, for example, that you have code such as the following:

Dim i As Integer For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)    NegativeLBoundArray(i) = i Next

The For Next loop is acceptable because you are using LBound and UBound, and so the code automatically picks up the new lower and upper bounds for the array. The value that is being set into the array, however, is incorrect. The intent was to seed the array with values from 10 through 10, but the upgraded code is now seeding the array with values from 0 through 20.

To fix this problem, you could introduce an adjustment factor into your code and apply it to any code that is using hard-coded values to access an array element. The example above uses a calculated value i based on the array index. We could adjust i by subtracting 10. If we define our adjustment value in a constant, the updated code is

Dim i As Integer Const NegativeLBoundArray_Adjustment = 10 For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)    NegativeLBoundArray(i) = i - NegativeLBoundArray_Adjustment Next

With the adjustment in place, the code once again seeds the array with values from 10 through 10.

There is another way to solve this problem. If it s easier for you to use negative array bounds, or simply any nonzero lower bound, consider using the .NET Framework System.Array class. This class includes a method called CreateInstance, which allows you to create a generic System.Array with nonzero lower bounds. If we start with the original Visual Basic 6 array declarations given earlier:

Dim OptionOneBasedArray(10) As Integer Dim PositiveLBoundArray(10 To 20) As Long Dim NegativeLBoundArray(-10 To 10) As Variant

we can convert, in Visual Basic .NET, the declarations to System.Array and include calls to CreateInstance to initialize the type and dimensions of the arrays as follows:

Dim OptionOneBasedArray As System.Array  ' 1 to 10 As Short Dim PositiveLBoundArray As System.Array  ' 10 to 20 As Integer Dim NegativeLBoundArray As System.Array  '  10 to 10 As Object ' Arrays to define the array length and LBound for each dimension ' Since we are only dealing with single dimension arrays, we only  ' need one element Dim ArrLens(0) As Integer Dim LBounds(0) As Integer ArrLens(0) = 10 LBounds(0) = 1 OptionOneBasedArray = System.Array.CreateInstance(GetType(Short), _    ArrLens, LBounds) ArrLens(0) = 11 LBounds(0) = 10 PositiveLBoundArray = System.Array.CreateInstance(GetType(Integer), _     ArrLens, LBounds) ArrLens(0) = 21 LBounds(0) = -10 NegativeLBoundArray = System.Array.CreateInstance(GetType(Object), _    ArrLens, LBounds) Dim i As Integer For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)    NegativeLBoundArray(i) = i Next

A caveat when using System.Array is that you cannot assign a System.Array variable containing a nonzero lower bound to a strongly typed array. For example, the following code will result in a Specified cast is not valid exception. The exception occurs on the assignment of the strongly typed array variable StrongTypeArray to the System.Array variable NegativeLBoundArray.

Dim StrongTypeArray() As Object StrongTypeArray = NegativeLBoundArray

Structures

You will not find the Visual Basic 6 Type End Type, commonly referred to as a user-defined type, in Visual Basic .NET. Instead you will find Structure End Structure, a superset of Type End Type. Within a structure, you can define members of any type. In addition, you can define methods and properties. You will find that a Visual Basic .NET structure works much like a class. One difference is that you cannot create instances of a structure, and all structure members implicitly include the Shared attribute. This means that a structure can access shared members of the class or module where it is defined, but it cannot access instance members.

Member Initialization

A difference between Visual Basic 6 and Visual Basic .NET user-defined types is that Visual Basic 6 supports fixed-length array declarations within a user-defined type. In Visual Basic .NET, however, you cannot specify the size of the array contained within a user-defined type. To work around this limitation, you can define a constructor for your user-defined type in which you initialize the array to a fixed size. Consider the following Visual Basic 6 code:

Private Type MyType    MyStringArray(10) As String End Type Sub Main()    Dim mt As MyType    MsgBox (mt.MyStringArray(0)) End Sub

The Upgrade Wizard upgrades this code to

Private Structure MyType    <VBFixedArray(10)> Dim MyStringArray() As String           'UPGRADE_TODO: "Initialize" must be called to initialize    'instances of this structure.    Public Sub Initialize()       ReDim MyStringArray(10)    End Sub End Structure     Public Sub Main()    'UPGRADE_WARNING: Arrays in structure mt may need to be    'initialized before they can be used.    Dim mt As MyType    MsgBox(mt.MyStringArray(0)) End Sub

The wizard has done a few things here to create the equivalent Visual Basic .NET code. It has added the VBFixedArray attribute to the structure, specifying the size of the array. This attribute is useful if you are passing the structure to a Visual Basic file function such as Get or Put. The Visual Basic file function will use the attribute to determine whether it needs to read or write additional header information associated with the array. Another change the wizard has made is to insert a public method within the structure called Initialize. The code contained within the Initialize method includes ReDim calls to initialize fixed-length arrays contained within the structure. Finally, the wizard has inserted some comments to let you know that you should include a call to the Initialize method before attempting to use the structure.

If you run the code as upgraded, you will encounter a NullReferenceException, Object reference not set to an instance of an object, on the following line:

MsgBox(mt.MyStringArray(0))

The reason is that the structure member MyStringArray does not contain any array elements. To initialize the array with elements, you need to call the Initialize method on the structure. The best place to call Initialize is right after you declare a structure variable. In the example above, you should call Initialize right after the declaration of mt. After you apply this change, the updated code will be as follows:

Private Structure MyType    <VBFixedArray(10)> Dim MyStringArray() As String           Public Sub Initialize()       ReDim MyStringArray(10)    End Sub End Structure     Public Sub Main()    Dim mt As MyType    mt.Initialize()    MsgBox(mt.MyStringArray(0)) End Sub

LSet

The LSet statement can be used in Visual Basic 6 to assign the contents of one user-defined type to another. The assignment will work even if the two user-defined types do not contain the same fields. LSet works by copying the memory for the source user-defined type to the target user-defined type.

An example of where you might use LSet is to provide two different ways to view or access the same data. For example, suppose you want to store a version number in a 32-bit integer. The version number consists of 4 parts, each 1 byte in length major, minor, revision, and build version numbers. To simplify creating the 32-bit integer, you can create a user-defined type containing each part of the version number. For the purpose of storing the version in a 32-bit integer, you can use LSet to assign a user-defined type containing a 32-bit integer to the user-defined type containing the version parts. The following Visual Basic 6 code demonstrates how this can be done.

Type VersionPartsType    BuildVer As Byte    RevisionVer As Byte    MinorVer As Byte    MajorVer As Byte End Type Type VersionType    Version As Long End Type Sub Main()    Dim vp As VersionPartsType    Dim vt As VersionType         vp.MajorVer = 7    vp.MinorVer = 0    vp.RevisionVer = 1    vp.BuildVer = 2            LSet vt = vp    MsgBox "32-bit version number is " & Hex(vt.Version) End Sub

Visual Basic .NET does not support the LSet statement to assign one type to another. The Upgrade Wizard will place the following comment before calls to LSet in the upgraded code.

'UPGRADE_ISSUE: LSet cannot assign one type to another.

To solve this problem, you can create a copy function that you use to copy each member of one user-defined type to another user-defined type. In this case, you will need to write a copy function that combines four members of VersionPartsType into a single member of VersionType. Here is an example of a copy function you can use to take the place of the LSet statement:

Sub CopyVersion(ByVal vp As VersionPartsType, _                 ByRef vt As VersionType)    ' Calculate the 32-bit version number by placing each version part    ' within each byte of the 32-bit integer. Major version goes into     ' the top byte and build goes into the lowest byte.    vt.Version = vp.MajorVer * 2 ^ 24 + vp.MinorVer * 2 ^ 16 + _       vp.RevisionVer * 2 ^ 8 + vp.BuildVer End Sub

note

Note The LSet statement plays two roles: it left-aligns a string within a string, and it also sets a type to another type. The first role, left-aligning a string within a string, is supported in Visual Basic .NET.



Upgrading Microsoft Visual Basic 6.0to Microsoft Visual Basic  .NET
Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET w/accompanying CD-ROM
ISBN: 073561587X
EAN: 2147483647
Year: 2001
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net