Visual Basic

Memory Doesn't Matter

A common argument against Variants is that they take up more memory than do other data types. In place of an Integer, which normally takes just 2 bytes of memory, a Variant of 16 bytes is taking eight times more space. The ratio is less, of course, for other underlying types, but the Variant always contains some wasted space.

The question is, as with the issue of performance in the previous section, how significant is this? Again I think not very. If your program has some extremely large arrays-say, tens of thousands of integers-an argument could be made to allow Integers to be used. But they are the exception. All your normal variables in any given program are going to make no perceptible difference whether they are Variants or not.

I'm not saying that using Variants improves performance or memory. It doesn't. What I'm saying is that the effect Variants have is not a big deal-at least, not a big enough deal to outweigh the reasons for using them.

Type Safety

A more complex argument is the belief that Variants are poor programming style-that they represent an unwelcome return to the sort of dumb macro languages that encouraged sloppy, buggy programming.

The argument maintains that restricting variables to a specific type allows various logic errors to be trapped at compile time, an obviously good thing. Variants, in theory, take away this ability.

To understand this issue fully we must first look at the way non-Variant variables behave. In the following pages I have split this behavior into four key parts of the language, and have contrasted how Variants behave compared to simple data types in each of these four cases:

  • Assignment

  • Function Calls

  • Operators and Expressions

  • Visual Basic Functions

Case 1: Assignment between incompatible variables

Consider the following code fragment (Example A):

Dim i As Integer, s As String
  s = "Hello"
  i = s

What happens? Well, it depends on which version of Visual Basic you run. In pre-OLE versions of Visual Basic you got a Type mismatch error at compile time. In Visual Basic 6, there are no errors at compile time, but you get the Type mismatch trappable error 13 at run time when the program encounters the i = s line of code.

NOTE

Visual Basic 4 was rewritten using the OLE architecture; thus, versions 3 and earlier are "pre-OLE."

The difference is that the error occurs at run time instead of being trapped when you compile. Instead of you finding the error, your users do. This is a bad thing.

The situation is further complicated because it is not the fact that s is a String and i is an Integer that causes the problem. It is the actual value of s that determines whether the assignment can take place.

This code succeeds, with i set to 1234 (Example B):

Dim i As Integer, s As String
  s = "1234"
  i = s

This code in Example C does not succeed (you might have thought that i would be set to 0, but this is not the case):

Dim i as Integer, s As String
  s = ""
  i = s

These examples demonstrate why you get the error only at run time. At compile time the compiler cannot know what the value of s will be, and it is the value of s that decides whether an error occurs.

The behavior is exactly the same with this piece of code (Example D):

Dim i As Integer, s As String
  s = ""
  i = CInt(s)

As in Example C, a type mismatch error will occur. In fact, Example C is exactly the same as Example D. In Example C, a hidden call to the CInt function takes place. The rules that determine whether CInt will succeed are the same as the rules that determine whether the plain i = s will succeed. This is known as implicit type conversion, although some call it "evil" type coercion.

The conversion functions CInt, CLng, and so on, are called implicitly whenever there is an assignment between variables of different data types. The actual functions are implemented within the system library file OLEAUT32.DLL. If you look at the exported functions in this DLL, you'll see a mass of conversion functions. For example, you'll see VarDecFromCy to convert a Currency to a Decimal, or VarBstrFromR8 to convert a string from an 8-byte Real, such as a Double. The code in this OLE DLL function determines the rules of the conversion within Visual Basic.

If the CInt function had worked the same way as Val does, the programming world would've been spared a few bugs (Example E).

Dim i As Integer, s As String
  s = ""
  i = Val(s)

This example succeeds because Val has been defined to return 0 when passed the empty string. The OLE conversion functions, being outside the mandate of Visual Basic itself, simply have different rules (Examples F and G).

Dim i As Integer, s As String
  s = "1,234"
  i = Val(s)
  Dim i As Integer, s As String
  s = "1,234"
  i = CInt(s)

Examples F and G also yield different results. In Example F, i becomes 1, but in Example G, i becomes 1234. In this case the OLE conversion functions are more powerful in that they can cope with the thousands separator. Further, they also take account of the locale, or regional settings. Should your machine's regional settings be changed to German standard, Example G will yield 1 again, not 1234, because in German the comma is used as the decimal point rather than as a thousands separator. This can have both good and bad side effects.

These code fragments, on the other hand, succeed in all versions of Visual Basic (Examples H and I):

Dim i As Variant, s As Variant
  s = "Hello"
  i = s
  Dim i As Variant, s As Variant
  s = "1234"
  i = s

In both the above cases, i is still a string, but why should that matter? By using Variants throughout our code, we eliminate the possibility of type mismatches during assignment. In this sense, using Variants can be even safer than using simple data types, because they reduce the number of run-time errors. Let's look now at another fundamental part of the syntax and again contrast how Variants behave compared to simple data types.

LOCALE EFFECTS

Suppose you were writing a little calculator program, where the user types a number into a text box and the program displays the square of this number as the contents of the text box change.

Private Sub Text1_Change()
      If IsNumeric(Text1.Text) Then
          Label1.Caption = Text1.Text * Text1.Text
      Else
          Label1.Caption = ""
      End If
  End Sub

Note that the IsNumeric test verifies that it is safe to multiply the contents of the two text boxes without fear of type mismatch problems. Suppose "1,000" was typed into the text box-the label underneath would show 1,000,000 or 1, depending on the regional settings. On the one hand, it's good that you get this international behavior without performing any extra coding, but it could also be a problem if the user was not conforming to the regional setting in question. Further, to prevent this problem, if a number is to be written to a database or file, it should be written as a number without formatting, in case it is read at a later date on a machine where the settings are different.

Also, you should also avoid writing any code yourself that parses numeric strings. For example, if you were trying to locate the decimal point in a number using string functions, you might have a problem:

InStr(53.6, ".")

This line of code will return 3 on English/American settings, but 0 on German settings.

Note, finally, that Visual Basic itself does not adhere to this convention in its own source code. The number 53.6 means the same whatever the regional settings. We all take this for granted, of course.