Datatype Conversions


So far, this chapter has focused primarily on individual variables. However, when developing software, it is often necessary to take a numeric value and convert it to a string to display in a text box. Similarly, it is often necessary to accept input from a text box and convert this input to a numeric value. These conversions, unlike some, can be done in one of two fashions: implicitly or explicitly.

Implicit conversions are those that rely on the system taking the data at runtime and adjusting it to the new type without any guidance. Often, Visual Basic’s default settings allow developers to write code containing many implicit conversions that the developer may not even notice.

Explicit conversions, conversely, are those for which the developer recognizes the need to change a variable’s type and assign it to a different variable. Unlike implicit conversions, explicit conversions are easily recognizable within the code. Some languages such as C# require that essentially all conversions that might be type unsafe be done through an explicit conversion; otherwise, an error is thrown.

It is therefore important to understand what a type-safe implicit conversion is. In short, it’s a conversion that by nature of the data involved cannot fail. For example, if you assign the value of a smaller type, Short, into a larger type, Long, then there is no way this conversion can fail. As both values are integerstyle numbers, and the maximum and minimum values of a Short variable are well within the range of a Long, this conversion will always succeed and can safely be handled as an implicit conversion:

  Dim shortNumber As Short = 32767 Dim longNumber As Long = shortNumber 

However, the reverse of this is not a type-safe conversion. In a system that demands explicit conversions, the assignment of a Long value to a Short variable results in a compilation error, as the compiler doesn’t have any safe way of handling the assignment when the larger value is outside the range of the smaller value. It is still possible to explicitly cast a value from a larger type to a smaller type, but this is an explicit conversion. By default, Visual Basic supports certain unsafe implicit conversions. Thus, adding the following line will not, by default, cause an error under Visual Basic:

  shortNumber = longNumber 

This is possible for two reasons. One is based on Visual Basic’s legacy support. Previous versions of Visual Basic supported the capability to implicitly cast across types that don’t fit the traditional implicit casting boundaries. It has been maintained in the language because one of the goals of Visual Basic is to support rapid prototyping. In a rapid prototyping model, a developer is writing code that “works” for demonstration purposes but may not be ready for deployment. This distinction is important because in the discussion of implicit conversions, you should always keep in mind that they are not a best practice for production software.

Implicit Conversions and Compiler Options

As noted in the introduction to this section, Visual Basic supports certain unsafe implicit conversions. This capability is on by default but can be disabled in two ways. The first method is specific to each source file and involves adding a line to the top of the source file to indicate to the compiler the status of Option Strict.

The following line will override whatever the default project setting for Option Strict is for your project. However, while this can be done on a per-source listing basis, this is not the recommended way to manage Option Strict. For starters, consistently adding this line to each of your source files isn’t a good practice:

  Option Strict On 

The preferred method to manage the Option Strict setting is to change the setting for your entire project. Without going into details of the XML associated with your project file, the easiest way to accomplish this is to use Visual Studio 2005. Visual Studio 2005 and the various versions of this tool are discussed in more detail in Chapter 13; however, for completeness, the compilation settings are discussed in this context.

Visual Studio 2005 includes a tab on the Project Settings page to edit the compiler settings for an entire project. You can access this screen by right-clicking the project in the Solution Explorer and selecting Properties from the context menu. As noted in the preceding chapter, the Project Properties dialog has a Compile tab. When you select this tab, you should see a window similar to the one shown in Figure 2-4.

image from book
Figure 2-4

Aside from your default project file output directory, this page contains several compiler options. These options are covered here because the Option Explicit and Option Strict settings directly impact your variable usage:

  • Option Explicit - This option has not changed from previous versions of Visual Basic. When enabled, it ensures that every variable is explicitly declared. Of course, if you are using Option Strict, then this setting doesn’t matter because the compiler won’t recognize the type of an undeclared variable. To my knowledge, there’s no good reason to ever turn this option off.

  • Option Strict - When this option is enabled, the compiler must be able to determine the type of each variable, and if an assignment between two variables requires a type conversion - for example, from Integer to Boolean - the conversion between the two types must be expressed explicitly. This setting can be edited by adding an Option Strict declaration to the top of your source code file. The statement within a source file applies to all of the code entered in that source file, but only to the code in that file.

  • Option Compare - This option determines whether strings should be compared as binary strings or whether the array of characters should be compared as text. In most cases, leaving this as binary is appropriate. Doing a text comparison requires the system to convert the binary values that are stored internally prior to comparison. However, the advantage of a text-based comparison is that the character “A” is equal to “a” because the comparison is case insensitive. This enables you to perform comparisons that don’t require an explicit case conversion of the compared strings. In most cases, however, this conversion still occurs, so it’s better to use binary comparison and explicitly convert the case as required.

In addition to setting Option Explicit, Option Strict, and Option Compare to either On or Off for your project, Visual Studio 2005 allows you to customize specific compiler conditions that may occur in your source file. Thus, it is possible to leverage individual settings, such as requiring early binding as opposed to runtime binding, without limiting implicit conversions. These individual settings are part of the table of individual compiler settings listed below the Option Strict setting.

Notice that as you change your Option Strict setting, the notifications with the top few conditions are automatically updated to reflect the specific requirements of this new setting. In general, this table lists a set of conditions that relate to programming practices you might want to avoid or prevent, and should definitely be aware of. The use of warnings for the majority of these conditions is appropriate, as there are valid reasons why you might want to use or avoid each.

The basic idea is that these conditions represent possible runtime error conditions that the compiler can’t truly detect, except to identify that an increased possibility for error exists. Selecting Warning for a setting bypasses that practice, as the compiler will warn you but allow the code to remain. On the other hand, setting a practice to Error prevents compilation.

An example of why these conditions are noteworthy is the warning on accessing shared member variables. If you are unfamiliar with shared member values, they are part of the discussion of classes in Chapter 3. At this point, it’s just necessary to know that these values are shared across all instances of a class. Thus, if a specific instance of a class is updating a shared member value, it is appropriate to get a warning to that effect. The action is one that can lead to errors, as new developers sometimes fail to realize that a shared member value is common across all instances of a class, so if one instance updates the value, then the new value is seen by all other instances.

While many of these conditions are only addressed as individual settings, Visual Studio 2005 carries forward the Option Strict setting. Most experienced developers agree that using Option Strict and being forced to recognize when type conversions are occurring is a good thing. Certainly, when developing software that will be deployed in a production environment, anything that can be done to help prevent runtime errors is desirable. However, Option Strict can slow the development of a program because you are forced to explicitly define each conversion that needs to occur. If you are developing a prototype or demo component that has a limited life, you might find this option limiting.

If that were the end of the argument, then many developers would simply turn the option off the current default and forget about it, but Option Stricthas a runtime benefit. When type conversions are explicitly identified, the system performs them faster. Implicit conversions require the runtime system to first identify the types involved in a conversion and then obtain the correct handler.

Another advantage of Option Strict is that during implementation developers are forced to consider every place a conversion might occur. Perhaps the development team didn’t realize that some of the assignment operations resulted in a type conversion. Setting up projects that require explicit conversions means that the resulting code tends to have type consistency to avoid conversions, thus reducing the number of conversions in the final code. The result is not only conversions that run faster, but also, it is hoped, a smaller number of conversions as well.

Performing Explicit Conversions

So, if you aren’t going to use implicit conversions, then what’s necessary to implement explicit conversions? Keep in mind that even if you choose to allow implicit conversions, these are only allowed for a relatively small number of datatypes. At some point you’ll need to carry out explicit conversions. The following code is an example of some typical conversions between different integer types when Option Strict is enabled:

  Dim myShort As Short Dim myUInt16 As UInt16 Dim myInt16 As Int16 Dim myInteger As Integer Dim myUInt32 As UInt32 Dim myInt32 As Int32 Dim myLong As Long Dim myInt64 As Int64 myShort = 0 myUInt16 = Convert.ToUInt16(myShort) myInt16 = myShort myInteger = myShort myUInt32 = Convert.ToUInt32(myShort) myInt32 = myShort myInt64 = myShort myLong = Long.MaxValue If myLong < Short.MaxValue Then   myShort = Convert.ToInt16(myLong) End If myInteger = CInt(myLong) 

The preceding snippet provides some excellent examples of what might not be intuitive behavior. The first thing to note is that you can’t implicitly cast from Short to UInt16, or any of the other unsigned types for that matter. That’s because with Option Strict the compiler won’t allow an implicit conversion that might result in a value out of range or loss of data. You may be thinking that an unsigned Short has a maximum that is twice the maximum of a signed Short, but in this case, if the variable myShort contained a -1, then the value wouldn’t be in the allowable range for an unsigned type.

Just for clarity, even with the explicit conversion, if myShort were a negative number, the Convert .ToUInt32 method would throw a runtime exception. Managing failed conversions requires either an understanding of exceptions and exception handling, as covered in Chapter 9, or the use of a conversion utility such as TryParse, covered later in this section.

The second item illustrated in this code is the shared method MaxValue. All of the integer and decimal types have this property. As the name indicates, it returns the maximum value for the specified type. There is a matching MinValue method for getting the minimum value. As shared properties, the properties can be referenced from the class (Long.MaxValue) without requiring an instance.

Finally, although this code will compile, it won’t always execute correctly. It illustrates a classic error, which in the real world is often intermittent. The error occurs because the final conversion statement does not check to ensure that the value being assigned to myInteger is within the maximum range for an integer type. On those occasions when myLong is larger than the maximum allowed, this code will throw an exception.

Visual Basic has many ways to convert values. Some of them are updated versions of techniques that are supported from previous versions of Visual Basic. Others, such as the ToString method, are an inherent part of every class (although the .NET specification does not guarantee how a ToString class is implemented for each type).

The following set of conversion methods is based on the conversions supported by Visual Basic. They coincide with the primitive datatypes described earlier; however, continued use of these methods is not considered a best practice. That bears repeating: While you may find the following methods in existing code, you should strive to avoid and replace these calls:

CBool()

CByte()

CChar()

CDate()

CDbl()

CDec()

CInt()

CLng()

CObj()

CShort()

CSng()

CStr()

Each of these methods has been designed to accept the input of the other primitive datatypes (as appropriate) and to convert such items to the type indicated by the method name. Thus, the CStr class is used to convert a primitive type to a String. The disadvantage of these methods is that they have been designed to support any object. This means that if a primitive type is used, then the method automatically boxes the parameter prior to getting the new value. This results in a loss of performance. Finally, although these are available as methods within the VB language, they are actually implemented in a class (as with everything in the .NET Framework). Because the class uses a series of type-specific overloaded methods, the conversions run faster when the members of the Convert class are called explicitly:

  Dim intMyShort As Integer = 200 Convert.ToInt32(intMyShort) Convert.ToDateTime("9/9/2001") 

The classes that are part of System.Convert implement not only the conversion methods listed earlier, but also other common conversions. These additional methods include standard conversions for things such as unsigned integers and pointers.

All the preceding type conversions are great for value types and the limited number of classes to which they apply, but these implementations are oriented toward a limited set of known types. It is not possible to convert a custom class to an Integer using these classes. More importantly, there should be no reason to have such a conversion. Instead, a particular class should provide a method that returns the appropriate type. That way, no type conversion is required. However, when Option Strict is enabled, the compiler requires you to cast an object to an appropriate type before triggering an implicit conversion. Note, however, that the Convert method isn’t the only way to indicate that a given variable can be treated as another type.

Parse and TryParse

Most value types, at least those which are part of the .NET Framework, provide a pair of shared methods called Parse and TryParse. These methods accept a value of your choosing and then attempt to convert this variable into the selected value type. Note that the Parse and TryParse methods are only available on value types. Reference types have related methods called DirectCast and Cast, which are optimized for reference variables.

The Parse method has a single parameter. This input parameter accepts a value that is the target for the object you are looking to create of a given type. This method then attempts to create a value based on the data passed in. However, be aware that if the data passed into the Parse method cannot be converted, then this method will throw an exception that your code needs to catch. The following line illustrates how the Parse function works:

  result = Long.Parse("100") 

Unfortunately, when you embed this call within a Try-Catch statement for exception handling, you create a more complex block of code. Because you always need to encapsulate such code within a Try-Catch block, the .NET development team decided that it would make more sense to provide a version of this method that encapsulated that exception handling logic.

This is the origin of the TryParse method. The TryParse method works similarly to the Parse method except that it has two parameters and returns a Boolean instead of a value. Instead of assigning the value of the TryParse method, you test it as part of an If-Then statement to determine whether the conversion of your data to the selected type was successful. If the conversion was successful, the new value is stored in the second parameter passed to this method, which you can then assign to the variable you want to hold that value:

  Dim converted As Long If Long.TryParse("100", converted) Then     result = converted End If 

CType

The CType method accepts two parameters. The first parameter is the object that is having its type cast, and the second parameter is the name of the object to which it is being cast. This system enables you to cast objects from parent to child types or from child to parent types. There is a limitation to the second parameter in that it can’t be a variable containing the name of the casting target. Casting is defined at compile time, and any form of dynamic name selection would occur at runtime. An example of casting is shown as part of the discussion of working with the Object class earlier in this chapter.

Support for a runtime determination of object types is based on treating variables as objects and using the object metadata and the TypeOf operator to verify that an object supports various method and property calls. The CType method accepts both value and reference types. More detailed information regarding its use is presented in Chapter 3.

DirectCast and TryCast

The DirectCast method works similarly to the CType method, with a couple of minor differences. First, unlike CType, the DirectCast method accepts only reference types. This is because the DirectCast method is tied much more closely to objects and the use of inheritance and interfaces. Additionally, in order to make it perform faster, DirectCast does not include any logic to actually check for and convert an object to the requested type. The DirectCast method is meant to allow your code to take an object that has been cast as its base type of object and recast it in its original form. Similar to CType, these methods are covered in more detail in Chapter 3.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

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