Data is a funny word—although not as funny as datum. Our minds are filled with data: the useful and useless trivia that clogs thought; the millions of memories that keep superficial conversations going strong. But the word data rarely comes up in conversation. Unless you are a computer junkie, or you hang around the office all hours of the day or night waiting for reports of crunched numbers, you never have a need to use the term. I have never been asked to lend someone a cup of data. My friends never try to judge my health by asking, “How’s your data going?” And you almost never hear it used as a character name in popular science fiction television shows.
Despite its lack of usage in everyday communication, data is extremely important. In the programming world, it is everything. In this chapter, we will discuss how Visual Basic uses and manipulates data within your applications, and how you can master the tools that make this manipulation possible.
In Chapter 2, I mentioned how all data in a computer eventually breaks down to individual bits, electrical impulses that represent either 1 or 0, on or off, true or false. Since our decimal number system requires more than just those two values, computers work in the world of binary—a number system limited to only the numbers 0 and 1. Fortunately, it’s pretty easy to represent basic decimal integer numbers using binary notation. You probably remember Mrs. Green back in second grade telling you about the different place values of multidigit numbers, shown in Figure 6-1.
The same type of diagram can be used for binary numbers; only the position names and values are changed. For convenience, we call these positions by their decimal names, or use the related powers of two. All of this is shown in Figure 6-2.
To figure out what this number is in decimal, just add up the columns. Let’s see, there’s one each of fours, eights, and sixty-fours, and none of the rest; 4 + 8 + 64, that’s 76. Since any binary digit can never be more than 1, the counting is pretty simple. I showed an 8-bit (8-digit) binary example here—which can handle the numbers 0 through 255—but you can represent larger decimal numbers by adding more binary digits.
That’s just fine for integer values, but how do you represent decimal and fractional numbers? What about negative numbers; where do they fit in this binary system? And it’s not just numbers. My computer can process text data, arrays of numbers, graphical images, and customer records. How are those stored in binary form?
To handle myriad data forms, every computer includes a small community of Lilliputians who are good at math, language, and art. No wait, I think that’s from a story I’m reading my son at bedtime. Oh yes, now I remember. Computers implement data types to handle all the various forms of data to be managed. Each data type acts as an interpreter between a collection of bits and a piece of information that a computer user can better utilize and understand.
All data types ultimately store their content as individual bits of data, but they differ in how those bits get interpreted. Imagine a data type named Vitamin
that indicated which vitamins were included in a food product. Figure 6-3 shows how the 8 bits used earlier could be assigned and interpreted as vitamins.
With such a data type, you could assign vitamin values to food items tracked in your application. (This is just a sampling of vitamins; you would require more bits to handle all of the vitamins. This example should not be construed as an offer of medical services. Consult your doctor.)
For an example that is more in tune with Visual Basic, take that number 76 we were discussing earlier. It’s easy enough to convert it to binary representation, as in 01001100. The .NET Framework includes a few data types that do this conversion automatically, varying only by the number of binary digits (bits) they can handle. In the computer world, 76 also represents a letter of the alphabet—the capital letter L. That’s because there’s a data type that establishes a dictionary between binary values and alphabetic (and other) characters. Windows programs have long used ASCII (American Standard Code for Information Interchange) as its number-to-character dictionary. This 8-bit system documents how to convert the numbers 0 through 255 into all the various characters used in English, including punctuation and other miscellaneous characters. Another dictionary, Unicode, uses 16 bits of data to handle around 65,000 different characters. .NET uses Unicode for its character and “string” data types.
Another rule-bearing data type is Boolean, which uses a single bit to represent either True (a bit value of 1
) or False (0
). Negative integers, floating-point and fixed-point decimal values, and dates and times round out the kinds of basic data most often managed by computers and their applications. More complex data structures can be built up from these basic types.
All data types in .NET are implemented as classes within the System
namespace. One such data type is System.Byte
, which implements an 8-bit integer value, just like we discussed earlier. It holds integer values from 0 to 255. These values are always stored using 8 bits of binary data, but they magically appear in decimal form whenever you ask them to be presented.
The .NET Framework includes 15 core interpretive data types: 8 for integers, 3 for decimal numbers, 2 for character data, a combined data type for dates and times, and a Boolean data type.
Based on the number of available data types (8 out of the 15 core types), you would think that most programmers worked with integers all day long—and you’d be right. Whether it’s actual user data or loop counters or status codes or the storage method for enumerated data types, integers show up everywhere in .NET code.
The range of values for an integer data type depends directly on the number of binary digits managed by that data type; the more digits, the bigger the range. Also, half of the integer data types store both positive and negative values (called “signed” integers), whereas the other half support only positive numbers (“unsigned”). Table 6-1 lists the eight integer data types included with .NET, and their associated ranges.
Table 6-1. Integer data types in .NET
.NET data type | Bits | Style | Range of values |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Looking at these types another way, Table 6-2 shows the relationship between the types and their number of bits and range style.
Once upon a time, life was happy. Strangers said hello when they met you on the street. Succulent fruit burst forth from the trees. In short, God was in His heaven, and everything was right with the world—and then along came fractions. At first, they didn’t seem that bad, since so many of them could be easily converted into a plain numeric form by inserting a decimal point in the number: ½ became 0.5; ¼ became the longer yet smaller 0.25; ⅓ became 0.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 . . . hey, what’s going on here? I can’t write all those 3s. The book would be 2,000 pages, or more. Eventually people discovered that in many cases, it just wasn’t worth the bother of writing out all the 3s, so they just stopped at some point, as in 0.33333333. It wasn’t perfectly accurate, but it was good enough.
This is what life is like for computer-based decimal values. You can have perfect accuracy—up to a point. After that, you have to settle for good enough. The .NET Framework includes three decimal data types. Two of them accept limited accuracy in exchange for a large range of values. The third has perfect accuracy, but its range is more limited. Table 6-3 documents these three types.
Table 6-3. An accurate list of the inaccurate decimal data types
.NET data type | Accuracy | Range | Description |
---|---|---|---|
|
|
| The The more digits you have on the left of the decimal, the fewer you have available for the right of the decimal, and vice versa. For numbers with no decimal portion, the range is from −79,228,162,514,264,337,593,543,950,335 to 79,228,162,514,264,337,593,543,950,335. (That’s 29 digits, but who’s counting?) For numbers with only zero (0) to the left of the decimal, the range is −0.0000000000000000000000000001 to 0.0000000000000000000000000001. |
|
|
| The |
|
|
| The |
Hey, check this out. ktuefghbiokh. Pretty cool, eh? That’s the power of a computer in action managing text data. So efficient; so graceful; so lskjdfljsdfjl. Although computers are really number machines, they handle text just as well. Of course, it’s really just you doing all the wordsmithing. In fact, the computer isn’t even smart enough to tell the difference between numbers and letters; it’s all bits to the CPU. Pretty mindless, if you ask me. I mean, what’s the use of having all that computing power if you can’t even think?
Despite all their speed and technology, computers are still just lumps of silicon wrapped up in a nice package. The computer I’m typing on doesn’t even know that I’m insulting it; I can type these things on and on, and there’s nutten that thiz komputre cann due about itt.
The framework includes two text-related data types: System.Char
and System.String
. The Char
data type holds a single character, no more, no less. At 16 bits, it holds any of the thousands of Unicode characters.
The String
data type allows up to about two billion Unicode characters to be “strung” together into one long text block. Strings in .NET are immutable; once you create a string, it cannot be changed in any way. If you want to add text to an existing string, .NET will instead create a brand-new string built from the original two immutable strings.
Although Char
and String
are different data types, you can easily move data back and forth between them, since they are both based on basic Unicode characters.
The System.DateTime
data type lets you store either date or time values (or both) as data. Internally, DateTime
is just a simple integer counter that displays a converted date or time format when needed. As a number, it counts the number of “ticks” since 12:00 a.m. on January 1, 1 AD. Each “tick” is exactly 100 nanoseconds, so it’s pretty precise. The maximum allowed date is December 31, 9999 in the Gregorian calendar.
The System.Boolean
data type represents the true essence of computer data: the bit. It holds one of two possible values: True
or False
. Shockingly, the data type actually requires between 2 and 4 bytes of data space to keep track of that single bit of data.
It turns out that Boolean values are very important in programs. As a developer, you are always testing to see whether various conditions are met before you process a block of code. All of these conditions eventually boil down to Boolean values and operations. .NET even has ways to easily migrate data between integer values and the Boolean data type. In such conversions, 0 becomes False
, and the world of all other possible values becomes True
. When moving from Boolean to an integer equivalent, False
becomes 0 and True
becomes −1. (If you ever use the C# language, you’ll find that it converts True
to 1, not −1. Internally in .NET, True
does convert to 1, but for historical reasons, Visual Basic uses −1. This difference normally isn’t a problem unless you store Boolean values as integers in a disk file and expect both Visual Basic and C# programs to interpret the data correctly.)
You already knew that .NET is an object-oriented development environment. What you probably didn’t know is that some pranksters at Microsoft placed a bet to see whether they could make the entire .NET system one big derived class. Well, the group that said it could be done won the bet. Everything in .NET—all code and all data—is derived from a single base class: System.Object
. By itself, this class doesn’t have too many features. It can tell you its name, its type, and whether two instances of an object are in fact one and the same object. Other than that, it isn’t useful for much except to be used as a starting point for all other classes and types.
Because all classes in .NET—including all data types—derive from System.Object
, you can treat an instance of any class (or data type) as Object
. The data will remember what type it really is, so if you have a System.Int32
posing as System.Object
, you can change it back to System.Int32
later.
Back in Chapter 1, you read about the difference between value types and reference types: value types are buckets that contain actual data, and reference types contain instructions on where you can find the actual data. In general, value types contain simple and small data values, whereas reference types point to large and complex data blocks. This isn’t always true, but for most data you work with, it will be true.
System.Object
is a reference type from which all other types and classes derive. This includes all the core data types, so you would think that they would be reference types as well. But there is another class stuck in between System.Object
and most of the Visual Basic data types. This class, System.ValueType
, implements the basic definition and usage of a value type. Table 6-4 lists some of the differences between value and reference types.
Table 6-4. Value type and reference type usage
Value types | Reference types |
---|---|
Ultimately derive from | Ultimately derive from |
Derived core data types: | Derived core data type: |
Provide support for Visual Basic “classes.” | |
Enumerations are derived as follows: | Delegates, used as references to class methods, are derived as follows: |
Value types cannot derive from other classes or structures, nor can further structures derive from them. | Reference types can be derived from other classes, and can be used as base classes. |
Instances cannot be set to | Instances can be set to |
Instances can only contain data of the specified type. For instance, | Instances usually refer to data of their defined type, but an instance can also point to a derived type. For example, an instance of |
Do not go through the full .NET garbage collection process. | Are destroyed through garbage collection. |
(In addition to classes and structures, Visual Basic also defines “modules.” The .NET documentation identifies modules as reference types, but you can’t create instances of them.)
A value type can only contain data of its own type, but reference types can point to derived instances. This is important in .NET, since it was designed to allow a System.Object
instance to refer to any data in an application. System.Object
instances can refer to either value type or reference type data. For reference types, this is easy to understand since that instance will just point to some derived instance of itself. But if you assign a value type to a System.Object
reference, .NET has to mark that instance in a special way to indicate that a reference type contains a value type. This process is called boxing, and the reverse process is called unboxing. Although boxing is useful, and sometimes essential, it comes with a substantial performance hit.
All the data types implemented in the Visual Basic language are wrappers for the core .NET data types. Only some of the names have been changed to protect the innocent. Table 6-5 lists the Visual Basic data types and their .NET equivalents.
Table 6-5. Visual Basic data types and related .NET types
Visual Basic type | .NET type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All the Visual Basic data types are fully interchangeable with their .NET equivalents. Any instance of System.Int32
can be treated as though it were an instance of Integer
, and vice versa.
The quickest way to include values of a particular data type in your Visual Basic code is to use a literal. You’ve already seen literals in action in this book. Chapter 1 included a literal in its sample project.
MsgBox("Hello, World!")
This call to the MsgBox
function includes a String literal. String literals always appear within a set of double quotes. Most numeric literals appear with a data-type-defining character on the end of the literal, but there are other variations. Table 6-6 lists the different literal values you can include in your code.
Table 6-6. Literals supported by Visual Basic
Literal type | Example | Description |
---|---|---|
|
| The |
|
| Single-character literals appear in double quotes with a trailing character c. A literal of type |
|
| Date or time literals appear between a set of number signs. You can include dates, times, or a combination of both. The date or time values can be in any format recognized by Windows, although Visual Studio may reformat your date literal for conformity with its own standards. |
|
| Floating-point values of type |
|
| Floating-point values of type |
Hexadecimal |
| You can include hexadecimal literals in your code by starting the value with the “&H” character sequence, followed by the hex digits. |
|
| Integral values of type |
|
| Integral values of type |
|
| You can include octal literals in your code by starting the value with the “&O” character sequence, followed by the octal digits. |
|
| Integral values of type |
|
| Floating-point values of type |
|
| String literals appear within a set of double quotes, with no special character following the closing quote. Use two quote characters within the string literal to embed a single quotation mark. |
Literals are nice, but it isn’t always clear what they mean. Encountering the number 12 in a formula, for instance, might cause the formula to generate correct results, but it would still be helpful to know what 12 means. Is it the number of months in a year, the number of hours in a day, the minimum number of teeth in a mouth to eat steak, or something even more sinister?
Constants provide a way to assign meaningful names to literal values. They are treated a lot like literal values, but once defined, they can be used over and over again in your code. Each use of a literal value, even if it has the same value, represents a distinct definition and instance of that value.
In Visual Basic, constants are defined using the Const
keyword.
Const MonthsInYear As Short = 12
This constant definition has the following parts:
In this case, the name is MonthsInYear
.
This example defines a Short
constant. The data type always follows the As
keyword. If you leave out this As
clause, the constant’s data type will be whatever the assigned literal would have been on its own. Only the following data types can be used for constants: Boolean
, Byte
, Char
, Date
, Decimal
, Double
, Integer
, Long
, Object
, SByte
, Short
, Single
, String
, UInteger
, ULong
, UShort
, or the name of an enumeration (discussed in the next section).
The initializer assigned here is 12
. Once assigned, this value cannot be altered while your code is running. Constants are always value types, not reference types. Initializers are usually simple literals, but you can also include simple calculations:
Const Seven As Integer = 3 + 4
The definition of MonthsInYear
listed here represents the typical format of a constant definition included within a code procedure. You can also define constants outside procedures, but still within a class or other type. When you do this, you generally add an access modifier keyword just before the Const
keyword. This keyword indicates how much code will be able to use the constant. I’ll describe access modifiers a little later, in the section on variables. Constants defined within a procedure can only be used within that procedure.
Once you define a constant, you can use it anywhere you would use an equivalent literal.
Const GreatGreeting As String = "Hello, World!" ...Later... MsgBox(GreatGreeting)
Enumerations, one of the core .NET types, allow you to group together named, related integer values as a set. Once bound together, the enumeration can be used like any other data type; you can create variables that are specific instances of an enumeration.
Enumerations are a multiline construct; the first line defines the name and underlying data type of the enumeration. Each enumeration member appears on a separate line, ending with a final closing End Enum
line.
01 Enum CarType As Integer 02 Sedan = 1 03 StationWagon = 2 04 Truck = 3 05 SUV = 4 06 Other = 5 07 End Enum
The declaration line (line 01) includes the Enum
keyword, the name of the enumeration (CarType
), and the underlying data type (Integer
). The As
data type clause is optional; if you leave it off, the enumeration defaults to Integer
. If you do supply a data type, it must be one of the following: Byte
, Integer
, Long
, SByte
, Short
, UInteger
, ULong
, or UShort
.
Each member of the enumeration (lines 02 to 06) must include at least a member name (such as Sedan
). You can optionally assign a numeric value to some or all of the members, as I have done in the sample. If a member lacks an assignment, it is set to one more than the previous member. If none of the members have an assigned value, the first is assigned 0, the next 1, and so on.
Once defined, enumeration members act a lot like integer constants; you can use them anywhere you would normally use a literal or constant. When referencing the members of an enumeration in your code, include both the enumeration name and the member name.
CarType.Sedan
The Enum
statement cannot be used within a method or procedure. Instead, you define an enumeration as a member of a type (class, structure, or module), or as its own standalone type, just like a class. The .NET Framework includes many useful predefined enumerations intended for use with framework features. For instance, the System.DayOfWeek
enumeration includes members for each day of the week.
Literals are nice, and constants and enumerations are nicer, but none of them can be altered once your program starts. This tends to make your application rigid and inflexible. If all your customers are named “Fred” and they only place orders for $342.34, it probably won’t be much of a limitation. But most users want more variety in their software. Variables are named containers for data, just like constants, but their contents can be modified throughout the run of an application. Also, they can contain both value types and reference types. Here’s the basic syntax for defining a new variable:
Dim customerName As String
The Dim
keyword—originally from the word dimension—defines a new variable; in this case, a variable named customerName
with a data type of String
. This named container is ready to hold any String
value; assign to it string literals, other string variables, or the return value from functions that generate strings. Since it is a reference type, it can also be set to Nothing
, a special Visual Basic value and keyword that means “this reference type is empty, really empty.”
customerName = Nothing ' Nothing customerName = "Fred" ' Literal customerName = GetCustomerName(customerID) ' Function result
All variables contain their default value until set to something else. For reference types and nullable types, the default is Nothing
; for numeric values, the default is 0. Booleans default to False
. You can include an initial assignment as part of the Dim
statement to override the default assignment.
Dim countdownSeconds As Short = 60 Dim processingDate As Date = Today Dim customerName As String = GetCustomerName(customerID)
The last line in that code block shows a reference type—String
—being assigned the String
result of a function. You can also assign a brand-new instance of a reference instance to a reference type variable. And it’s new. That is, it uses the special New
keyword, which says, “I’m creating a new instance of the specific data type.” There are a few different variations, but they all produce the same results.
' ----- One-line variation. Dim someEmployee As New Employee ' ----- Another one-line variation. Dim someEmployee As Employee = New Employee ' ----- Two-line variation. Dim someEmployee As Employee someEmployee = New Employee
Remember that reference types are buckets that contain directions for locating the actual data. When a reference variable first springs into existence, it contains Nothing
. That is, the bucket contains no instructions at all since there is no related data stored anywhere. When you assign a new instance to a reference type variable, that instance gets stored somewhere in memory, and instructions for locating that data are dumped into the bucket. In the previous code block, each use of the New
keyword creates a new data instance somewhere in memory. This data’s location is then assigned to the someString
variable.
Many classes include one or more constructors, initialization routines that set up the initial values of the instance. You can call a specific constructor through the New
clause. The String
data type includes constructors that let you build an initial string. One of these special constructors lets you create a new string containing multiple copies of a specific character. The following statement assigns a string of 25 asterisks to the lotsOfStars
variable:
Dim lotsOfStars As New String("*"c, 25)
Constructors are discussed in detail in Chapter 8.
Dim
statements can appear anywhere in a procedure, but by tradition they appear right at the start of a procedure, before any other logic statements.
Sub MyProcedure( ) Dim myVariable As Integer ' ----- Additional code goes here... End Sub
As with constants, variables can be defined either within a procedure, or outside a procedure but within a type. (Variables and constants declared outside a procedure are known as fields. Variables and constants declared inside a procedure are known as local variables and local constants, respectively.) The Dim
keyword is always used with in-procedure variable declarations. At the type level, the Dim
keyword is replaced by one of the following access modifiers:
Private
Private
variables can be used by any member or procedure within the type, but nowhere else. If you derive a new class from a base class that includes a private type variable, the code in that derived class will have no access at all to that Private
variable; it won’t even know it exists.
Friend
Friend
variables are private to an assembly. They can be used by any code in their related type, but also by any code anywhere in the same assembly. Now that’s friendly.
Public
Public
variables are available everywhere. It is possible to write an application or component that exposes its types to code beyond itself. Anything marked Public
can be exposed in this way.
Protected
Protected
variables are like Private
type variables, but code in derived classes can also access them. You can use the Protected
keyword only in a class definition; it doesn’t work in a structure or module.
Protected Friend
Protected Friend
variables combine all the features of Friend
and Protected
. They can be used only in classes.
A single class or type may contain both fields and local variables and constants.
Class MyClass ' ----- Here's a field. Private InternalUseOnly As Boolean Sub MyProcedure( ) ' ----- Here's a local variable. Dim myVariable As Integer End Sub End Class
There are other syntax variations to the Dim
statement, some of which I will discuss later in this chapter and in other chapters.
When you define a variable within a procedure, it has procedure-level scope. This means you can use the variable anywhere within that procedure. Your procedure will likely have “block statements,” those statements, such as For...Next
and If...Then
, that require more than one line of source code to complete. If you add a Dim
statement between the starting and ending lines of one of these statements, that declared variable will have only block-level scope. It will be available only within that block of the procedure.
For counter = 1 To 10 Dim processResult As Integer ' ----- More code here. Next counter MsgBox(processResult) ' This line will fail
This code declares processResult
within the For...Next
block. So, it’s available only for use inside that block; any attempted use of processResult
outside the For
block generates an immediate error.
The lifetime of a procedure-level variable begins when the code first enters that procedure, and ends when the code exits the procedure. This is true for both procedure-level and block-level variables. This means that if you assign a block-level variable some value before exiting the block, it will still have that value if you reenter that block during the same procedure call.
For fields (class-level variables), the scope depends on the access level used when declaring the variable. The lifetime of a field begins when the class instance is created in code, and ends when the instance is destroyed or goes completely out of use.
The names that you give to your variables will not have that much impact on how your application runs on the user’s workstation, but they can affect the clarity of the source code. In the days before .NET, many Windows programming languages used a system called Hungarian Notation to craft variable names. Such names helped to communicate information about the data type and usage of a variable to anyone reading the source code. Unfortunately, the rules used to define Hungarian variable names were somewhat complex, and varied not only among programming languages, but also among programmers using the same language.
When Microsoft released .NET back in 2002, its documentation included various programming recommendations. One of those recommendations was “Stop using the Java programming language.” Another recommendation encouraged programmers to cease from using Hungarian Notation, and instead embrace a new system that used casing rules to differentiate variables. The rules state that all variable names should employ mixed-case names (where each logical word in the variable name starts with a capital letter and continues with lowercase letters). The only differentiation comes in the capitalization of the initial letter:
In the interest of full disclosure, I must tell you that I modified the original recommendations slightly from the documentation supplied with Visual Studio. The original rules were a little more complex when it came to field and method parameter names. Personally, I find the two rules listed here to be adequate for my needs.
You might give a local variable a name like lookInThisVariable
, which capitalizes the first letter of each word, but not the initial letter. If you defined this variable as a field instead, you would change its name to LookInThisVariable
, capitalizing the first letter.
Visual Basic is a strongly typed language. This means that all data values are either Integer
, or Short
, or String
, or some other specific data type. Even the default Object
data type is considered strong. To create a variable without a data type would be weak, and Visual Basic programmers are anything but weak.
Normally, you specifically tell Visual Basic what data type to use for a variable. But a new Visual Basic 2008 feature called local type inference lets the Visual Basic compiler join in the fun of assigning data types to variables. And what fun it is!
In standard variable declaration, you include the data type with an As
clause.
Dim whatAmI As String whatAmI = "You're a string, and nothing but a string."
But with local type inference, Visual Basic will figure out the data type all on its own when you leave off the As
clause.
Dim thing1 Dim thing2 thing1 = "This is a string." thing2 = 25 MsgBox(thing1.GetType.ToString) MsgBox(thing2.GetType.ToString)
When you run this code, two messages appear to tell you the strong-type-name of each thing: System.String
and System.Int32
, respectively. (Don’t worry about the “GetType” stuff for now. It just identifies the true type of the things.) Visual Basic acts as though the first two lines looked like this:
Dim thing1 As String Dim thing2 As Integer
Once Visual Basic identifies the data type for one of the as-of-yet-untyped variables, that variable is glued to that type. The following code will fail:
Dim thing1 thing1 = "This is a string." thing1 = 25 ' This fails, since thing1 is a string.
As the name implies, local type inference works only with local variables. Class fields must be declared with a specific data type. Other restrictions apply. See dealer for details.
You can turn the type inference system on and off using the Option Infer
statement at the top of each source code file.
Option Infer On
You can also set this on a project-wide basis through the Compile tab of the project properties.
Type inference exists to support the new LINQ features discussed in Chapter 17. Although you can let Visual Basic infer most or all of the variables in your application, it is not a good thing to do in practice. As smart as the compiler is, it doesn’t think deeply about the overall logic of your application, and it may make different data typing choices than you would. For instance, Visual Basic may infer a variable as Integer
, even though you plan to stuff large Long
values into it later. If you have the opportunity to include meaningful and accurate As
clauses with your Dim
statements, do it. Because I said so. Because it’s the right thing to do.
Visual Basic includes a variety of operators that let you manipulate the values of your variables. You’ve already seen the assignment operator (=
), which lets you assign a value directly to a variable. Most of the other operators let you build up expressions that combine multiple original values in formulaic ways for eventual assignment to a variable. Consider the following statement:
squareArea = length * width
This statement includes two operators: assignment and multiplication. The multiplication operator combines two values (length
and width
) using multiplication, and the assignment operator stores the product in the squareArea
variable. Without operators, you would be hard-pressed to calculate an area or any complex formula.
There are two types of non-assignment operators: unary and binary. Unary operators work with only a single value, or operand. Binary operators require two operands, but result in a single processed value. Operands include literals, constants, variables, and function return values. Table 6-7 lists the different operators with usage details.
Table 6-7. Visual Basic non-assignment operators
Operator | Description |
---|---|
| Addition. Adds two operands together, producing a sum. Some programmers also use this operator to perform string concatenation, but it’s better to join strings using another operator ( Syntax: Example: |
| Unary plus. Ensures that an operand retains its current sign, either positive or negative. Since all operands automatically retain their sign, this operator is usually redundant. It may come in handy when we discuss “operator overloading” in Chapter 12. Syntax: Example: |
| Subtraction. Subtracts one operand (the second) from another (the first), and returns the difference. Syntax: Example: |
| Unary negation. Reverses the sign of its operand. When used with a literal number, it results in a negative value. When used with a variable that contains a negative value, it produces a positive result. Syntax: Example: |
| Multiplication. Multiplies two operands together, and returns the product. Syntax: Example: |
| Division. Divides one operand (the first) by another (the second), and returns the quotient. If the second operand contains zero, a divide-by-zero error occurs. (When working with Syntax: Example: |
Integer division. Divides one operand (the first) by another (the second), and returns the quotient, first truncating any decimal portion from that result. If the second operand contains zero, a divide-by-zero error occurs. (See the caveat listed with the Syntax: Example: | |
| Modulo. Divides one operand (the first) by another (the second), and returns the remainder as an integer value. If the second operand contains zero, a divide-by-zero error occurs. (See the caveat listed with the Syntax: Example: |
| Exponentiation. Raises one operand (the first) to the power of another (the second). Syntax: Example: |
| String concatenation. Joins two operands together, and returns a combined string result. Both operands are converted to their Syntax: Example: |
| Conjunction. Performs a logical or bitwise conjunction on two operands, and returns the result. For logical (Boolean) operations, the result will be Syntax: Example: |
| Disjunction. Performs a logical or bitwise disjunction on two operands, and returns the result. For logical (Boolean) operations, the result will be Syntax: Example: |
| Short-circuited conjunction. This operator is equivalent to the logical version of the Syntax: Example: |
| Short-circuited disjunction. This operator is equivalent to the logical version of the Syntax: Example: |
| Negation. Performs a logical or bitwise negation on a single operand. For logical (Boolean) operations, the result will be Syntax: Example: |
| Exclusion. Performs a logical or bitwise “exclusive or” on two operands, and returns the result. For logical (Boolean) operations, the result will be Syntax: Example: |
| Shift left. The Syntax: Example: |
| Shift right. The Syntax: Example: |
| Equals (comparison). Compares two operands and returns Syntax: Example: |
| Not equals. Compares two operands and returns Syntax: Example: |
| Less than. Compares two operands and returns Syntax: Example: |
| Greater than. Compares two operands and returns Syntax: Example: |
| Less than or equal to. Compares two operands and returns Syntax: Example: |
| Greater than or equal to. Compares two operands and returns Syntax: Example: |
| Pattern comparison. Compares the first operand to the pattern specified in the second operand, and returns Syntax: Example: |
| Type comparison. Compares the first operand to another object, a data type, or Syntax: Example: |
| Negated type comparison. This operator is a shortcut for using the first IsNot second Not (first Is second) Syntax: Example: |
| Instance comparison. Returns the data type of a value or variable. The type of every class or data type in .NET is implemented as an object, based on Syntax: Example: |
| Delegate retrieval. Returns a delegate (described in Chapter 8) that represents a specific instance of a procedure or method. Syntax: Example: |
| Type retrieval. Returns the data type of a value or variable, just like the Syntax: Example: |
Non-assignment operators use their operands to produce a result, but they do not cause the operands themselves to be altered in any way. The assignment operator does update the operand that appears on its left side. In addition to the standard assignment operator, Visual Basic includes several operators that combine the assignment operator with some of the binary operators. Table 6-8 lists these assignment operators.
Table 6-8. Visual Basic assignment operators
Operator | Based on |
---|---|
| Standard assignment operator |
|
|
| − (subtraction) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
These assignment operators are just shortcuts for the full-bodied operators. For instance, to add 1
to a numeric variable, you can use either of these two statements:
' ----- Increment totalSoFar by 1. totalSoFar = totalSoFar + 1 ' ----- Another way to increment totalSoFar by 1. totalSoFar += 1
Normally, the lifetime of a local procedure-level variable ends when the procedure ends. But sometimes you might want a variable to retain its value between each call into the procedure. Sometimes you might also want a million dollars, but you can’t always have it. But you can have variables that keep their values if you want. They’re called static variables. To declare a static variable, use the Static
keyword in place of the Dim
keyword.
Static keepingTrack As Integer = 0
The assignment of 0
to keepingTrack
is done only once, when creating the instance of the type that contains this statement. Thereafter, it keeps whatever value is assigned to it until the instance is destroyed. Static variables can only be created within procedures.
Software applications often work with sets of related data, not just isolated data values. Visual Basic includes two primary ways of working with such sets of data: collections (discussed in Chapter 16) and arrays. An array assigns a numeric position to each item included in the set, starting with 0 and ending with one less than the number of items included. An array of five items has elements numbering from 0 to 4.
As an example, imagine that you were developing a zoo simulation application. You might include an array named animals
that includes each animal name in your zoo:
Animal #0: Aardvark
Animal #1: Baboon
Animal #2: Chimpanzee
Animal #3: Donkey
. . . and so on . . .
Visual Basic identifies array elements by a parenthesized number after the array name. For our animals, a simple assignment puts the String
name of each animal in an array element.
animal(0) = "Aardvark" animal(1) = "Baboon" animal(2) = "Chimpanzee" animal(3) = "Donkey"
Using each array element is just as easy.
MsgBox("The first animal is: " & animal(0))
Each element of an array is not so different from a standalone variable. In fact, you could just consider the set of animals in the example code to be distinct variables: a variable named animal(0)
, another variable named animal(1)
, and so on. But they are better than ordinary variables because you can process them as a set. For instance, you can scan through each element using a For...Next
loop. Consider an Integer
array named eachItem
with elements numbered from 0 to 2. The following code block adds up the individual items of the array as though they were distinct variables:
Dim totalAmount As Integer totalAmount = eachItem(0) + eachItem(1) + eachItem(2)
But since the items are in a numbered array, you can use a For...Next
loop to scan through each element, one at a time.
Dim totalAmount As Integer = 0 For counter As Integer = 0 to 2 ' ----- Keep a running total of the items. totalAmount += eachItem(counter) Next counter
Before you assign values to array elements, or retrieve those elements, you must declare and size the array for your needs. The Dim
statement creates an array just as it does ordinary variables; the ReDim
statement resizes an array after it already exists.
Dim animal(0 To 25) As String ' 26-element array Dim moreAnimals( ) As String ' An undefined String array ReDim moreAnimals(0 To 25) ' Now it has elements
Normally, the ReDim
statement would wipe out any existing data stored in each array element. Adding the Preserve
keyword retains all existing data.
ReDim Preserve moreAnimals(0 to 30) ' Keeps elements 0 to 25
Each element of the array is an independent object that can be assigned data as needed. In this example, each element is a String
, but you can use any value type or reference type you wish in the array declaration. If you create an array of Object
elements, you can mix and match the data in the array; element 0
need not contain the same type of data as element 1
.
The array itself is also an independent object—a class instance that manages its set of contained elements. If you need to specify the entire array, and not just one of its elements (and there are times when you need to do this), use its name without any parentheses or positional values.
Visual Basic arrays support more than one dimension (or “rank”). The dimensions indicate the number of independent ranges supported by the array. A one-dimensional array, like the animal
array earlier, includes a single range. A two-dimensional array includes two comma-delimited ranges, forming a grid arrangement of elements, with separate ranges for rows and columns.
Dim ticTacToeBoard(0 To 2, 0 To 2) As Char ' 3 × 3 board
An array can have up to 60 different dimensions, although there are usually better ways to organize data than breaking it out into that many dimensions.
The lower bound of any array dimension is normally 0, as indicated by the 0 To
×
clause when defining or redimensioning the array. You can actually leave the “0 To” part out of the statement, and just include the upper bound.
' ----- These two lines are equivalent. Dim animal(0 To 25) As String Dim animal(25) As String
These two statements both create an array with 26 elements, numbered 0 through 25.
There are a few special cases where nonzero lower bounds are allowed, such as when working with older COM-generated arrays. But the standard Visual Basic declaration syntax does not allow you to create arrays with nonzero lower bounds.
To determine the current lower or upper bound of an array dimension, use the LBound
and UBound
functions.
MsgBox("The board is " & (UBound(ticTacToeBoard, 1) + 1) & _ " by " & (UBound(ticTacToeBoard, 2) + 1))
If your array includes only a single dimension, you don’t have to tell LBound
or UBound
which dimension you want to check.
MsgBox("The upper element is numbered " & UBound(animal))
Each array also includes GetLowerBound
and GetUpperBound
methods that return the same results as LBound
and UBound
. (I discuss methods in detail in Chapter 8.) However, the dimension number you pass to the GetLowerBound
and GetUpperBound
methods starts from 0, whereas LBound
and UBound
dimension values start the counting at 1.
MsgBox("The board is " & _ (ticTacToeBoard.GetUpperBound(0) + 1) & _ " by " & (ticTacToeBoard.GetUpperBound(1) + 1))
Once you’ve declared your array elements, you can store and retrieve elements whenever you need. It’s also possible to store elements in your array right at declaration time. The list of new array elements appears in a set of curly braces.
Dim squares( ) As Integer = {0, 1, 4, 9, 16, 25}
You must leave out the lower and upper bound specifications when creating an array in this way. The squares
array shown here will have elements numbered 0 to 5.
Value types are hard-working variables, maintaining their data values throughout their lives. Reference types work hard, too, but they can be filled with Nothing
and get a little rest time. This difference has long been a thorn in the side of value types. Is it too much to ask to give these working-class variables a little down time?
Well, Microsoft has heard this plea, and starting with Visual Basic 2008, value types can now be assigned with Nothing
. These new nullable types are essential when you want to have an undefined state for a standard value type (especially useful when working with database fields). Consider this class that manages employee information:
Public Class Employee Public Name As String Public HireDate As Date Public FireDate As Date Public Sub New(ByVal employeeName As String, _ ByVal dateHired As Date) Me.Name = employeeName Me.HireDate = dateHired End Sub End Class
This class works well, except that FireDate
is not really correct. By default, FireDate
will be set to January 1, 1, at midnight, and you can use that date as your “never fired” date. But what happens if your company really did fire someone just at that moment, two thousand years ago?
To resolve this issue, nullable types let you assign and retrieve Nothing
from value type variables. These vitamin-enriched value types are declared using a special question-mark syntax.
' ----- Either of these two statements will work. Public FireDate As Date? Public FireDate2? As Date
I prefer the first syntax, with the question mark added to the data type. But either statement will work. Once it’s declared, a value type can take either standard data or Nothing
.
FireDate = Nothing FireDate = #7/18/2008# If (FireDate Is Nothing) Then...
There is a special syntax when defining your own custom value types as nullable, but since it uses the “generics” Visual Basic feature, I’ll wait to introduce it until Chapter 16.
This final section includes a brief listing of the functions built into the Visual Basic language, many of which you will use regularly in your applications. Also listed here are some members of the Framework Class Library (FCL) that replicate features that were part of the Visual Basic language before .NET, but were moved into the framework for more general access. For the exact syntax required to use these functions, access the Visual Studio online help.
The conversion functions allow you to convert data of one Visual Basic data type to another. It’s not a free-for-all, so don’t go converting the string "hello"
to an integer and expect it to work. But converting numbers from one numeric type to another, or converting numbers between string and numeric types, generally works just fine.
All of these statements (except CType
) have the same basic syntax:
dest
= CXxxx
(source
)
where source
is the value to be converted by C
Xxxx
. You don’t have to assign the result to a variable; you can use the result anywhere you would use a similar literal or variable value. Table 6-9 lists the built-in conversion functions.
Table 6-9. Visual Basic conversion functions
Function | Description |
---|---|
| Converts a value to a |
| Converts a value to a |
| Converts a value to a |
| Converts a value to a |
| Converts a value to a |
| Converts a value to a |
| Converts a value to an |
| Converts a value to a |
| Converts a value to an |
| Converts a value to an |
| Converts a value to a |
| Converts a value to a |
| Converts a value to any defined type, class, or interface, either in your application or in the FCL. The syntax is: CType(
where CType(5, String) converts the |
| Converts a value to a |
| Converts a value to a |
| Converts a value to a |
Visual Basic includes several functions designed to manage date and time values. Table 6-10 lists these functions. Most of these functions accept one or more source arguments, and return either Date
, String
, or a numeric result.
Table 6-10. Visual Basic date-related functions and properties
Description | |
---|---|
| Adds or subtracts a time or date value to a starting date. For instance, you can add 12 minutes, or subtract three years, from a given date. |
| Returns the difference between two date or time values. You can specify the interval, such as months or seconds. |
| Returns one component of a date or time, such as the hour or the year. |
| Returns a |
| Returns the current date as a string. You can also set the date on the local computer using this keyword. |
| Returns the date portion of a combined date and time value; the time portion is discarded. |
| Returns the day from a given date value. |
| Formats a given date or time as a string, using a small set of predefined formats. This function is included for backward compatibility with older VBScript code. |
| Returns the hour from a given time value. |
| Indicates whether the data supplied to this function is a valid date. |
| Returns the minute from a given time value. |
| Returns the month from a given date value. |
| Returns the name of a month for a numeric month value, 1 through 12. |
| Returns the current date and time. Equivalent to |
| Returns the seconds from a given time value. |
| Returns the current date and time. Equivalent to |
| Returns the number of seconds that have elapsed since midnight of the current day. This function is reset to 0 each midnight. |
| Returns a |
| Returns the current time as a string. You can also set the time on the local computer using this keyword. |
| Returns the time portion of a combined date and time value; the date portion is discarded. |
| Returns the current date. |
| Returns an integer that indicates the day of the week. |
| Returns the name of a weekday for an integer day of the week. |
| Returns the year from a given date value. |
Variables created as System.DateTime
(or Visual Basic Date
) each include several properties and methods that provide features similar to the functions listed in Table 6-10. For instance, the Second
property returns the number of seconds.
Dim meetingTime As Date meetingTime = #11/7/2005 8:00:03am# MsgBox(meetingTime.Second) ' Displays '3' MsgBox(Second(meetingTime)) ' Also displays '3'
You can use either the intrinsic Visual Basic functions or the equivalent System.DateTime
methods and properties in your code. Each technique provides the same result.
Visual Basic programmers just love working with numbers; it’s in their blood. Fortunately, Visual Basic includes lots of features for working wonders with numbers. In addition to the standard data manipulation operators, Table 6-11 lists several number-related functions.
Table 6-11. Visual Basic number-related functions
Function | Description |
---|---|
| Truncates the decimal portion of a number, returning only the whole portion. Similar to the |
| Formats a given number as a currency value, using a small set of predefined formats. This function is included for backward compatibility with older VBScript code. |
| Formats a given number as a general number, using a small set of predefined formats. This function is included for backward compatibility with older VBScript code. |
| Formats a given number as a percentage, using a small set of predefined formats. This function is included for backward compatibility with older VBScript code. |
| Formats a number as hexadecimal, and returns its string representation. |
| Returns the whole number that is less than or equal to the supplied value. Similar to the |
| Indicates whether the data supplied to this function is a valid number. |
| Formats a number as octal, and returns its string representation. |
| Extracts the first valid number from a string and returns it. |
The .NET Framework includes the System.Math
class, which contains several math-related function members. Some of these, such as Round
, Sin
, and Log
, were implemented as intrinsic functions in Visual Basic 6.0, but have been moved from the language to the Math
class in .NET.
Visual Basic also includes several functions used for financial and accounting calculations. These functions were also included in Visual Basic 6.0. As they are not relevant to the project discussed in this book, I will only list their names here: DDB
, FV
, IPmt
, IRR
, MIRR
, NPer
, NPV
, Pmt
, PPmt
, PV
, Rate
, SLN
, and SYD
.
String manipulation is a core part of Windows programming. The new XML features included with .NET are really just fancy string-manipulation routines, although with the complexities hidden from view. Visual Basic includes many functions designed to manipulate strings and characters. They are listed in Table 6-12.
As with most functions, these functions return a new string or value, leaving the original string or source values intact. The lone exception is the Mid
statement, which modifies the source variable’s value.
Table 6-12. Visual Basic string-related functions
Function | Description |
---|---|
| Returns the numeric ASCII or Unicode value for a character. |
| Given a number, these functions return the matching ASCII or Unicode character. |
| Returns an array that is a subset of a source array, but including only those elements that matched a pattern. |
| Formats number, date, and time values using predefined or custom formatting codes. |
| Extracts a single character from a larger string. |
| Returns the position of a substring within a larger string. |
| Returns the position of a substring within a larger string, searching from the end of the string until the beginning. |
| Returns a string built from a concatenation of an array of strings. |
| Converts a string to its lowercase equivalent. |
| Returns the leftmost portion of a string. |
| Returns the length of a string. |
| Left-aligns a string within a larger string of spaces. |
| Removes spaces from the start of a string. |
| Extracts a substring from the middle of a larger string. |
| Modifies a range of characters in an existing string with new content. This is not a function, but a special Visual Basic statement. Its syntax varies considerably from that of most other Visual Basic features. |
| Replaces occurrences of a substring with another substring, all within a larger string. |
| Returns the rightmost portion of a string. |
| Right-aligns a string within a larger string of spaces. |
| Removes spaces from the end of a string. |
| Generates a string containing a specified number of space characters. Similar to the |
| Splits a string into an array of substrings based on a delimiter. |
| Converts a number to its string representation. |
| Compares two strings, and returns an integer indicating their sort order. |
| Converts a string to a new format based on a conversion code. Some of the conversions involve changing the case of the content. |
| Generates a string containing a specified number of a given character. Similar to the |
| Reverses the characters in a string. |
| Removes spaces from the start and end of a string. |
| Converts a string to its uppercase equivalent. |
Variables created as System.String
(or Visual Basic String
) each include several properties and methods that provide features similar to the functions listed in Table 6-12. For instance, the Length
property returns the number of characters in the string.
Dim simpleString As String = "abcde" MsgBox(simpleString.Length) ' Displays '5' MsgBox(Len(simpleString)) ' Also displays '5'
You can use either the intrinsic Visual Basic functions or the equivalent System.String
methods and properties in your code. Each technique provides the same result, although the syntax details and options may vary.
Visual Basic includes several functions that refuse to be squeezed into any of the other categories. Table 6-13 documents these functions.
Table 6-13. Visual Basic miscellaneous functions
Description | |
---|---|
| Converts a value from one data type to another, although the starting and ending data types must be related. Similar to the |
| Returns the string representation of an error code. This works only with the system error codes previously available in Visual Basic 6.0, although these codes are still available in .NET. |
| Indicates whether the data supplied to this function is a valid array. |
| Indicates whether the data supplied to this function is a |
| Indicates whether the data supplied to this function is an error condition. |
| Indicates whether the data supplied to this function is undefined, or set to |
| Indicates whether the data supplied to this function is a reference type or a value type. |
| Returns a color code from a small set of predefined colors. |
| Returns a color code built from the individual red, green, and blue components. |
| Given a Visual Basic data type name, this function returns the equivalent .NET data type name. |
| Converts a value from one data type to another, although the starting and ending data types must be related. Similar to the |
| Returns a data type name that summarizes the data type of the supplied content. The returned string is a generalized summary, and not necessarily the “true” data type name. |
| Returns a code indicating the general data type of the supplied content. |
When you’re working with Visual Basic, you’re working with data. The data types included with Visual Basic are simply wrappers for the core data types in .NET, but Visual Basic also adds many functions and features that enhance your ability to manage and organize data.
You look tired. Why don’t you take a five-minute break, and then we’ll dive into the project code.
Welcome back! In this chapter, we’ll use the data type and function features we read about to design some general support routines that will be used throughout the program. All of this code will appear in a Visual Basic module named General
, all stored in a project file named General.vb.
Load the Chapter 6 (Before) Code project, either through the New Project templates or by accessing the project directly from the installation directory. To see the code in its final form, load Chapter 6 (After) Code instead.
I’ve already added the General.vb file with its module starting and ending blocks.
Friend Module General End Module
All the code we add in this chapter will appear between these two lines. Remember, modules are a lot like classes and structures, but you can’t create instances of them; all their members are shared with all parts of your source code. This allows them to be used anywhere in the application. We don’t need to do anything special to make them available to the entire program, other than to set the access level of each member as needed.
First, we’ll add some general constants used throughout the program. Back in Visual Basic 6.0, I would have called these “global constants.” But now they are simply shared members of the General
module. Add the following code just below the Module General
statement.
Insert Chapter 6, Snippet Item 1.
' ----- Public constants. Public Const ProgramTitle As String = "The Library Project" Public Const NotAuthorizedMessage As String = _ "You are not authorized to perform this task." Public Const UseDBVersion As Integer = 1 ' ----- Constants for the MatchingImages image list. Public Const MatchPresent As Integer = 0 Public Const MatchNone As Integer = 1 Public Enum LookupMethods As Integer ByTitle = 1 ByAuthor = 2 ...remaining items excluded for brevity... End Enum Public Enum LibrarySecurity As Integer ManageAuthors = 1 ...remaining items excluded for brevity... ViewAdminPatronMessages = 23 End Enum Public Const MaxLibrarySecurity As LibrarySecurity = _ LibrarySecurity.ViewAdminPatronMessages
These constants and enumerations are pretty self-explanatory based on their Pascal-cased names. UseDBVersion
will be used to ensure that the application matches the database being used when multiple versions of each are available. The MatchPresent
and MatchNone
constants will be used for library item lookups.
The two enumerations define codes that specify the type of library item lookup to perform (LookupMethods
), and the security codes used to limit the features that a specific administrator will be able to perform in the application (LibrarySecurity
).
It’s time to add some methods. The first method, CenterText
, centers a line of text within a specific width. For instance, if you had the string "Hello, World"
(12 characters in length) and you wanted to center it on a line that could be up to 40 characters long, you would need to add 14 spaces to the start of the line (determined by subtracting 12 from 40, and then dividing the result by 2). The routine uses a couple of the string-specific Visual Basic functions (such as Trim
, Left
, and Len
) to manipulate and test the data, and the integer division operator to help calculate the number of spaces to insert.
Insert Chapter 6, Snippet Item 2.
Public Function CenterText(ByVal origText As String, _ ByVal textWidth As Integer) As String ' ----- Center a piece of text in a field width. If the ' text is too wide, truncate it. Dim resultText As String resultText = Trim(origText) If (Len(resultText) >= textWidth) Then ' ----- Truncate as needed. Return Trim(Left(origText, textWidth)) Else ' ----- Start with extra spaces. Return Space((textWidth - Len(origText)) 2) & _ resultText End If End Function
The function starts by making a copy of the original string (origText
), removing any extra spaces with the Trim
function. It then tests that result to see whether it will even fit on the line. If not, it chops off the trailing characters that won’t fit, and returns that result. For strings that do fit on a line textWidth
characters wide, the function adds the appropriate number of spaces to the start of the string, and returns the result.
Code snippet #2 also added a function named LeftAndRightText
. It works just like CenterText
, but it puts two distinct text strings at the extreme left and right ends of a text line. Any questions? Great. Let’s move on.
Code snippet #3 adds a routine named DigitsOnly
. It builds a new string made of just the digits found in a source string, origText
. It does this by calling the IsNumeric
function for each character in origText
, one at a time. Each found digit is then concatenated to the end of destText
.
Insert Chapter 6, Snippet Item 3.
Public Function DigitsOnly(ByVal origText As String) As String ' ----- Return only the digits found in a string. Dim destText As String Dim counter As Integer ' ----- Examine each character. destText = "" For counter = 1 To Len(origText) If (IsNumeric(Mid(origText, counter, 1))) Then _ destText &= Mid(origText, counter, 1) Next counter Return destText End Function
The last two functions, CountSubStr
and GetSubStr
, count and extract substrings from larger strings, based on a delimiter. Visual Basic includes two functions, Mid
and GetChar
, that also extract substrings from larger strings, but these are based on the position of the substring. The CountSubStr
and GetSubStr
functions examine substrings by first using a delimiter to break the larger string into pieces.
Insert Chapter 6, Snippet Item 4.
The CountSubStr
function counts how many times a given substring appears in a larger string. It uses Visual Basic’s InStr
function to find the location of a substring (subText
) in a larger string (mainText
). It keeps doing this until it reaches the end of mainText
, maintaining a running count (totalTimes
) of the number of matches.
Public Function CountSubStr(ByVal mainText As String, _ ByVal subText As String) As Integer ' ----- Return a count of the number of times that ' a subText occurs in a string (mainText). Dim totalTimes As Integer Dim startPos As Integer Dim foundPos As Integer totalTimes = 0 startPos = 1 ' ----- Keep searching until we don't find it no more! Do ' ----- Search for the subText. foundPos = InStr(startPos, mainText, subText) If (foundPos = 0) Then Exit Do totalTimes += 1 ' ----- Move to just after the occurrence. startPos = foundPos + Len(subText) Loop ' ----- Return the count. Return totalTimes End Function
Just to be more interesting than I already am, I used a different approach to implement the GetSubStr
function. This function returns a delimited section of a string. For instance, the following statement gets the third comma-delimited portion of bigString
:
bigString = "abc,def,ghi,jkl,mno" MsgBox(GetSubStr(bigString, ",", 3)) ' Displays: ghi
I used Visual Basic’s Split
function to break the original string (origString
) into an array of smaller strings (stringParts
), using delim
as the breaking point. Then I return element number whichField
from the result. Since whichField
starts with 1 and the array starts at 0, I must adjust the position to return the correct element.
Public Function GetSubStr(ByVal origString As String, _ ByVal delim As String, ByVal whichField As Integer) _ As String ' ----- Extracts a delimited string from another ' larger string. Dim stringParts( ) As String ' ----- Handle some errors. If (whichField < 0) Then Return "" If (Len(origString) < 1) Then Return "" If (Len(delim) = 0) Then Return "" ' ----- Break the string up into delimited parts. stringParts = Split(origString, delim) ' ----- See whether the part we want exists and return it. If (whichField > UBound(stringParts) + 1) Then Return "" _ Else Return stringParts(whichField − 1) End Function
If these functions seem simple to you, great! Most Visual Basic code is no more difficult than these examples. Sure, you might use some unfamiliar parts of the FCL, or interact with things more complicated than strings and numbers. But the overall structure will be similar. Most source code is made up of assignment statements, tests using the If
statement, loops through data using a For...Next
or similar statement, and function calls. And that’s just what we did in these short methods.
3.129.218.69