VFP versions prior to VFP 8 don't have a native Collection Class. This is a place to discuss different implementations of collection classes in VFP prior to VFP 8.
There are two types of home grown collections in VFP
- Reference-based collections, where items are held in an array member.
- Contanership-based collections, where items are contained by the collection container by using
.AddObject()
.
Using a container class for a collection works well except that it can't support the kind of interface specified in the Collection topic. Using an array provides a much closer interface but isn't an object and doesn't have properties and methods.
Christof Wollenhaupt changed my mind on this by posting the following code on Compuserve: Pamela Thalacker
oData = NewObject("Collection")
oData.Items[3] = "three"
? oData.Items[3]
? oData.Items.Count
Define Class Collection as Custom
Dimension Items[3]
Procedure Items_Access(tnIndex)
If VarType(m.tnIndex) == "L"
Local loInfo
loInfo = NewObject("CollectionInfo")
loInfo.Count = ALen(This.Items)
Return loInfo
Else
Return This.Items[m.tnIndex]
Endif
EndProc
Enddefine
Define Class CollectionInfo as Custom
Count = 0
Enddefine
Hmm, much exactly as I already posted here at the end of this article.... -- Vlad Grynchyshyn
More to the point, ADDOBJECT()
inexorably conveys ownership and that's not something collections should intrinsically do. The base collection object should thus hold references, not values. Therefore forget containership unless there is a real ownership relationship between collection and collected. Thoughts?-- Steven Black
That make sense. The ownership relationship implies that when the parent is destroyed the owned objects are destroyed as well. Anything else?
What I'm trying to accomplish with these two topics is to determine the public interface(s) for collection classes ( Collection ) and the techniques for implementing these in VFP. -- ?jMM
Two other things I have seen done:
1. Items in the collection can be referenced as an array without using the Item method. For example, you could access the 3rd item in the collection as oCollection(3), instead of oCollection.Item(3).
This is not doable in VFP... in VB you can define a 'default' property and default 'Method.'
You can do this in VFP, but only using access/assign methods of the parent object when collection is really a property of that object. See sample in the files section at the Universal thread where I posted VFP collestion sample. General idea of it already described here at the end of this article. -- Vlad Grynchyshyn
2. Items can be accessed with non-numeric indices: if oCollection was a collection of form objects, then oCollection.Item("Invoice Form") or oCollection("Invoice Form") would return the form object in the collection with the caption "Invoice Form".
Codebook and Visual FoxExpress both have very good implementations of a collection that allow this. They have both a ReferenceCollection and a ChildCollection. The only thing you can't do in a VFP created collection is use For Each. Once again, VFE Solved this by creating an iterator for the collection class. It is a pretty robust collection class.
You can use For Each for my VFP collection. -- Vlad Grynchyshyn
-- Rick Hodder
After some investigation, I managed to make a collection-like property of object. Following is a simple sample of how to orginize collection-like access (Though he didn't highlight it, the neat feature here is improvized Default properties/methods!):
DEFINE CLASS coll AS custom
Name = "coll"
DIMENSION a[1]
PROCEDURE a_access
LPARAMETERS m.nIndex1
do case
case vartype(m.nIndex1) == 'N'
return THIS.a[m.nIndex1]
case vartype(m.nIndex1) == 'C'
return THIS.a[1]
case vartype(m.nIndex1) == 'L'
return THIS
endcase
ENDPROC
PROCEDURE a_assign
LPARAMETERS vNewVal, m.nIndex1
do case
case vartype(m.nIndex1) == 'N'
THIS.a[m.nIndex1] = m.vNewVal
case vartype(m.nIndex1) == 'C'
THIS.a[1] = m.vNewVal
case ISNULL(m.nIndex1)
THIS.a[1] = m.vNewVal
endcase
ENDPROC
ENDDEFINE
** Now, following are test commands:
oo = createobject("coll")
oo.a(1) = 5 && no error
? oo.a(1) && displays 5
? oo.a("qweqwe") && displays 5 anyway!
? oo.a.class && displays 'Coll'
? oo.a && displays 5
oo.a("qweqwe") = 1 && no error
? oo.a(1) && displays 1
oo.a.tag = '2'
? oo.a.tag && displays 2
&& Now, the main trick - allow to access objects in collection
oo.a(1) = createobject('coll')
? oo.a("qwe").class && displays 'Coll'
oo.a("qwe").a(1) = 5 && no error
? oo.a("qwe").a(1) && displays 5!
Using above approach, obviously, it is easy to make a collection property in VFP.
-- Vlad Grynchyshyn
Sample of use for each for above collection:
for each uitem in oo.a
...
endfor
See also Universal Thread files section where I posted full-functional VFP collection sample. -- Vlad Grynchyshyn.
There is also another collection object on the Universal Thread. It is simply titled "The best collection object in VFP". It was written by Rohullah Habibi and the download is just collection.zip. I have played with it a little, and it looks pretty good. -- Brian Mccord
Collections Library (ActiveX) for Visual FoxPro
http://www.news2news.com/vfp/?solution=12
The Collections ActiveX library includes Collection class with the interface similar to VFP's native Collection class.
LOCAL loItems as VfpCollections.VfpCollection
loItems = CREATEOBJECT('VfpCollections.VfpCollection')
loItems.Add('Sun')
loItems.Add('Mon')
loItems.Add('Tue')
* assuming a form is running
loItems.Add(_screen.Forms[1])
FOR EACH loItem IN loItems
? loItem
NEXT
? loItems.Contains('Mon') && returns True
? loItems.Search('Mon') && returns 2
nIndex = loItems.Search(_screen.Forms[1])
? loItems.Item(nIndex).Caption
The Stack and the Queue classes are also included.
-- Anatoliy Mogylevets
Category Class Design