Strange collection class behavior with objects

Alan Stevens asks:

Doug Kimzey discovered an odd behavior in the VFP collection class today. It only occurs when the collection members are objects. Try running the following code, and see if you can make sense of it. For extra confusion, uncomment the second call to THIS.ADD() and try to explain why it works the first time. J

I’ve changed the original code to be more clear.

It creates a subclass of the Collection class and calls the NewItem method twice to add two items to the collection. When the code runs, the Final Count shows only one item.

It makes a lot of sense to me when I examine it under the debugger<g>

Try putting a breakpoint on the Destroy, then look up the callstack to see that the object is being destroyed on the “THIS.oCurrent = oo” line. Comment out that line, and you get a count of 2.

When an object’s reference count goes to 0, then the object is Destroyed.

If object A references object B, VFP internally tracks that A references B. If you delete A, the ref count for B is decremented.

When you set a variable or property to an object, then the ref count is incremented. Any prior value is released. (If the prior value is an object, the ref count is decremented.)

Thus the assignment statement “THIS.oCurrent = oo” will release any prior object reference contained by THIS.oCurrent. The 2nd time through, the first object’s reference is removed.

Removing an object reference from a Collection class means any reference between the object and the collection is removed. That means the element is removed from the collection.

A simple fix (below) is to make the collection a member of another class, rather than inheriting from Collection.

o = createobject("testcollection")

o.NEWITEM()

o.NEWITEM()

?"Final count",o.count

 

DEFINE CLASS testcollection AS COLLECTION

  oCurrent = NULL

  FUNCTION NEWITEM()

    LOCAL oo as Object

    THIS.ADD(NEWOBJECT("MyCustom"))

    ?"Newitem count is " + TRANSFORM(THIS.COUNT)

    oo = THIS.ITEM(THIS.COUNT)

    ?"Post call to item count is " + TRANSFORM(THIS.COUNT)

    THIS.oCurrent = oo

    ?"Post property assignment count is " + TRANSFORM(THIS.COUNT)

    ?

ENDDEFINE

DEFINE CLASS MyCustom AS Custom

      PROCEDURE Destroy

            ?PROGRAM(),this.Name

ENDDEFINE

Simple fix:

o = createobject("testcollection")

o.NEWITEM()

o.NEWITEM()

?"Final count",o.oColl.count

 

DEFINE CLASS testcollection AS Custom

      ADD OBJECT oColl as collection

      oCurrent=NULL

  FUNCTION NEWITEM()

    LOCAL oo as Object

    THIS.oColl.ADD(NEWOBJECT("MyCustom"))

    ?"Newitem count is " + TRANSFORM(THIS.oColl.COUNT)

    oo = THIS.oColl.ITEM(THIS.oColl.COUNT)

    ?"Post call to item count is " + TRANSFORM(THIS.oColl.COUNT)

    THIS.oCurrent = oo

    ?"Post property assignment count is " + TRANSFORM(THIS.oColl.COUNT)

    ?

ENDDEFINE

DEFINE CLASS MyCustom AS Custom

      PROCEDURE Destroy

            ?PROGRAM(),this.Name

ENDDEFINE