ntil recently, Visual FoxPro developers wanting to use collections often created their own classes that were nothing more than fancy wrappers for arrays. However, in addition to being a lot of code to write, home-built collections don't support the FOR EACH syntax, which is especially awkward when they're exposed in COM servers. Visual FoxPro 8.0 solves this problem by providing a true Collection base class. Collections are a common way to store multiple instances of things. For example, a TreeView control has a Nodes collection and Microsoft Word has a Documents collection.
The Collection base class has only a few properties, events, and methods. The Add method adds an item to the collection, the Remove method removes an item, and the Item method returns an item. The Count property indicates how many items are in the collection. An "item" can be a scalar value such as text or a number, but most commonly is an object. In addition to the item itself, the collection can store a key for the item, such as a name. An item can be located in the collection by:
- Position: Collection.Item(2) returns the second item in the collection
- Key: Collection.Item('Doug') returns the item that has "Doug" as its key
Since Item is the default method, you can omit it if desired; Collection.Item(2)
do the same thing.
Collections can be simple replacements for arrays. Each item in a collection is similar to a row in an array. However, because they're objects, collections have many more capabilities than arrays. This article will look at three specific uses for collections.
Use Collections Instead of Arrays
Some objects need to store a collection of things. For example, a forms manager needs information about every open form in an application. In addition to an object reference to the form, it may also keep information about which toolbar the form uses (so you don't have multiple instances of the same toolbar), whether the form was added to the Window menu or not, the instance number of the form (in case the same form can be opened more than once), and so on. Until VFP 8, this information was often kept in an array, with one row per form and one column for each type of information.
However, as the number of columns increases, it becomes more and more difficult to keep track of what's in the array and where. Was it the fourth column that stored the instance number or the seventh? Also, because arrays in VFP can't have 0 rows, you have to be careful removing items from the array when a form is closed:
lnForms = alen(This.aForms, 1) ? 1
lnCols = alen(This.aForms, 2)
if lnForms = 0
This.aForms = .NULL.
* lnForm is the row number of the closed form
dimension This.aForms[lnForms, lnCols]
This complexity disappears when you use a collection. Instead of a row in an array, a form is represented by an object in a collection. The object contains a reference to the form and the other information required. Which code would you rather write (or read, for that matter) to find the instance number of a form?
* Array-based code
lnPos = ascan(This.aForms, 'CustomerForm')
lnRow = asubscript(This.aForms, lnPos, 1)
lnInstance = This.aForms[lnRow, 4]
* Collection-based code
lnInstance = ;
Removing an object from the collection is easy, because there's no need to worry about array dimensions. Simply call the Remove method of the collection.