Visual Basic Concepts

Collections in Visual Basic

What is a collection? In "The Visual Basic Collection Object," a collection was defined as a way of grouping related objects. That leaves a lot of room for interpretation; it's more of a concept than a definition.

In fact, as you'll see when you begin comparing collections, there are a lot of differences even among the kinds of collections provided in Visual Basic. For example, the following code causes an error:

Dim col As Collection
Set col = Forms      ' Error!

What's happening here? The Forms collection is a collection; the variable col is declared As Collection; why can't you assign a reference to Forms to the variable col?

The reason for this is that the Collection class and the Forms collection are not polymorphic; that is, you can't exchange one for the other, because they were developed from separate code bases. They don't have the same methods, store object references in the same way, or use the same kinds of index values.

This makes the Collection class's name seem like an odd choice, because it really represents only one of many possible collection implementations. This topic explores some of the implementation differences you'll encounter.

Zero-Based and One-Based Collections

A collection is either zero-based or one-based, depending on what its starting index is. As you might guess, the former means that the index of the first item in the collection is zero, and the latter means it's one. Examples of zero-based collections are the Forms and Controls collections. The Collection object is an example of a one-based collection.

Older collections in Visual Basic are more likely to be zero-based, while more recent additions are more likely to be one-based. One-based collections are somewhat more intuitive to use, because the index ranges from one to Count, where Count is the property that returns the number of items in a collection.

The index of a zero-based collection, by contrast, ranges from zero to one less than the Count property.

Index and Key Values

Many collections in Visual Basic allow you to access an item using either a numeric index or a string key, as the Visual Basic Collection object does. (Visual Basic's Collection object allows you to add items without specifying a key, however.)

The Forms collection, by contrast, allows only a numeric index. This is because there's no unique string value associated with a form. For example, you can have multiple forms with the same caption, or multiple loaded forms with the same Name property.

Adding and Removing Items

Collections also differ in whether or not you can add items to them, and if so, how those items are added. You can't add a printer to the Printers collection using Visual Basic code, for example.

Because the Collection object is a general-purpose programming tool, it's more flexible than other collections. It has an Add method you can use to put items into the collection, and a Remove method for taking items out.

By contrast, the only way to get a form into the Forms collection is to load the form. If you create a form with the New operator, or by referring to a variable declared As New, it will not be added to the Forms collection until you use the Load statement to load it.

The Forms and Controls collections don't have Remove methods. You add and remove forms and controls from these collections indirectly, by using the Load and Unload statements.

What Has It Got In Its Pocketses?

As noted above, a form is not added to the Forms collection until it's loaded. Thus the most accurate specification of the Forms collection is that it contains all of the currently loaded forms in the program.

Even that's not completely accurate. If your project uses Microsoft Forms (included for compatibility with Microsoft Office), you'll find those forms in a separate collection named UserForms. So the Forms collection contains all of the currently loaded Visual Basic forms in the program.

The contents of the Collection class are very precisely specified: anything that can be stored in a Variant. Thus the Collection object can contain an object or an integer, but not a user-defined type.

Unfortunately, this specification covers a lot of territory — a given instance of the Collection class could store any mongrel assortment of data types, arrays, and objects.

Tip   One of the most important reasons for creating your own collection classes, as discussed in "Creating Your Own Collection Classes," is so you can control the contents of your collections — a concept called type safety.

Enumerating a Collection

You can use For Each … Next to enumerate the items in a collection, without worrying about whether the collection is zero-based or one-based. Of course, this is hardly a defining characteristic of collections, because Visual Basic allows you to use For Each … Next to enumerate the items in an array.

What makes For Each … Next work is a tiny object called an enumerator. An enumerator keeps track of where you are in a collection, and returns the next item when it's needed.

When you enumerate an array, Visual Basic creates an array enumerator object on the fly. Collections have their own enumerator objects, which are also created as needed.

Enumerators Don't Skip Items

The enumerators of collections in Visual Basic don't skip items. For example, suppose you enumerate a collection containing "A," "B," and "C," and that while doing so you remove "B." Visual Basic collections will not skip over "C" when you do this.

Enumerators May Not Catch Added Items

If you add items to a collection while enumerating it, some enumerators will include the added items, while some will not. The Forms collection, for example, will not enumerate any forms you load while enumerating.

The Collection object will enumerate items you add while enumerating, if you allow them to be added at the end of the collection. Thus the following loop never ends (until you hit CTRL+BREAK, that is):

Dim col As New Collection
Dim vnt As Variant
col.Add "Endless"
col.Add "Endless"
For Each vnt In col
   MsgBox vnt
   col.Add "Endless"

On the other hand, items you add at the beginning of the collection will not be included in the enumeration:

Dim col As New Collection
Dim vnt As Variant
col.Add "Will be enumerated"
For Each vnt In col
   MsgBox vnt
   ' Add the item at the beginning.
   col.Add "Won't be enumerated", Before:=1

Why Enumerators?

By emitting a new enumerator each time a For Each … Next begins, a collection allows nested enumerations. For example, suppose you have a reference to a Collection object in the variable mcolStrings, and that the collection contains only strings. The following code prints all the combinations of two different strings:

Dim vnt1 As Variant
Dim vnt2 As Variant
For Each vnt1 In mcolStrings
   For Each vnt2 In mcolStrings
      If vnt1 <> vnt2 Then
         Debug.Print vnt1 & " " & vnt2
      End If

For More Information   See "Creating Your Own Collection Classes" later in this chapter.