11

The VBA Collection has 5 members, all of which are methods: Add, Count, Item, _NewEnum and Remove.

Of the 5 members, the Count method looks like it could/should be a getter rather than a method.

Is this a legacy of OOP/MS design from the early days of VB6? Did the designers of the Collection anticipate a time when Count might need to be a method, and designate Count as a method so that some future behavioral change wouldn't break the interface/COM?

EDIT As Jim Hewitt points out in the comments, the online documentation suggests that Count is a property, but Object Browser and the MIDL suggest otherwise.

Here's the documentation for VB6's Collection.Count Property

Returns a Long (long integer) containing the number of objects in a collection. Read-only.

But the VB6 Object Browser shows this: enter image description here

And in Excel 2016, Count sure looks lonely and like it wants to be a getter...

enter image description here

EDIT 22/Sep/16:

I referred back to Hardcore Visual Basic 5 by Bruce McKinney...

This doesn't exactly explain why Collection.Count is a method, but it does go some way to answering why I can't know why Collection.Count is a method:

On knowing the unknowable:

anything you think you know about the implementation of a Collection might be wrong for this version and probably will be wrong for the next version.

On Implementation details:

The Collection class provided with Visual Basic version 4 was a good 1 version. The Collection class provided with Visual Basic version 5 is not a good 2 version. Users offered many complaints and suggestions. None of them were implemented.

On Collections:

What is a Collection? You know its user interface, but you don’t really know what internal data structure makes it work. Normally, this isn’t a concern. Who cares how it works as long as it works? But, on second thought, the internal implementation is bound to affect the efficiency of operations. If some operations are less efficient than others, it might be nice to avoid the slow ones.

On Collection.Item:

Visual Basic could have made [Collection.Item] work [as a property]. (It was requested.)

ThunderFrame
  • 9,352
  • 2
  • 29
  • 60
  • 2
    Maybe because the number of elements in the collection is never stored, and never known from the class, and the only way to know it is to iterate through all the elements and count them, which the .Count method basically do ? Doesnt look strange to me... And yes everything in VBA is a legacy of VB6. .NET collections doesnt work that way. – Thomas G Sep 20 '16 at 09:52
  • 2
    Surely there's an internal field that let's the instance know its `Count` without iterating? – ThunderFrame Sep 20 '16 at 10:15
  • 1
    My bet is no, there is not. If there would be one, the Method would have no sense and that would be a property as you said. – Thomas G Sep 20 '16 at 10:21
  • 1
    note that if I would be sure, I would post this an answer and not a comment ;-) It's just my intuition here. – Thomas G Sep 20 '16 at 10:22
  • According to [this](https://msdn.microsoft.com/en-us/library/aa231021(v=vs.60).aspx) it is a read-only property. – Jim Hewitt Sep 20 '16 at 15:01
  • @JimHewitt - That doesn't jive with the typelib. – Comintern Sep 20 '16 at 23:02
  • So... let's fix the docs, aren't they on GitHub now? – Mathieu Guindon Sep 20 '16 at 23:04
  • 3
    My guess is it was a mistake. Certainly once they had released a version in which it was a method, they could not change it without breaking binary compatibility. So they had to live with the mistake. [It happens.](https://en.wikipedia.org/wiki/HTTP_referer#Etymology) – MarkJ Sep 21 '16 at 13:16

2 Answers2

5

As far as I can tell it actually is a method:

[
  odl,
  uuid(A4C46780-499F-101B-BB78-00AA00383CBB),
  helpcontext(0x000f7886),
  hidden,
  dual,
  oleautomation
]
interface _Collection : IDispatch {
    [id(00000000), helpcontext(0x000f7903)]
    HRESULT Item(
                    [in] VARIANT* Index, 
                    [out, retval] VARIANT* pvarRet);
    [id(0x00000001), helpcontext(0x000f7901)]
    HRESULT Add(
                    [in] VARIANT* Item, 
                    [in, optional] VARIANT* Key, 
                    [in, optional] VARIANT* Before, 
                    [in, optional] VARIANT* After);
    [id(0x00000002), helpcontext(0x000f7902)]
    HRESULT Count([out, retval] long* pi4);
    [id(0x00000003), helpcontext(0x000f7904)]
    HRESULT Remove([in] VARIANT* Index);
    [id(0xfffffffc)]
    HRESULT _NewEnum([out, retval] IUnknown** ppunk);
};

The "why" escapes me though.

Bob77
  • 13,167
  • 1
  • 29
  • 37
  • 1
    Indeed, but the "why" is central to the question. – ThunderFrame Sep 20 '16 at 23:17
  • 2
    @ThunderFrame I think the "why" could only he answered by Microsoft devs... who probably wonder the exact same thing too, looking at that dusty old code base... – Mathieu Guindon Sep 21 '16 at 00:33
  • 1
    It probably had something to do with avoiding the use of properties to keep marshaling overhead low. In this case doing so was probably just habit from applying that as a general practice. – Bob77 Sep 21 '16 at 01:06
0

It would be a guess, but Collection might have historically implemented count like System.Linq.Enumerable does. This might have been more historically relevant though, as now (as of 2018) the internal structure of collections has become known (discovered by the trick on VBForums).

Public Type VbCollectionKeyAndData
    Key                 As String
    Data                As Variant
End Type
'
Private Type VbCollectionHeader
    pInterface1         As Long   '  Ox00
    pInterface2         As Long   '  Ox04
    pInterface3         As Long   '  Ox08
    lRefCounter         As Long   '  Ox0C
    Count               As Long   '  Ox10
    pvUnk1              As Long   '  Ox14
    pFirstIndexedItem   As Long   '  Ox18
    pLastIndexedItem    As Long   '  Ox1C
    pvUnk4              As Long   '  Ox20
    pRootTreeItem       As Long   '  Ox24 ' This is actually a pointer to what's typically thought of as the root.
    pEndTreePtr         As Long   '  Ox28 ' This is effectively an EOF marker for the tree (bottom of it).  It points to the end of the VbCollectionHeader (HdrPtr + &h30)
    pvUnk5              As Long   '  Ox2C
End Type                          '  Ox30 ' Length.

Private Type VbCollectionItem
    Data                As Variant  '  Ox00
    KeyPtr              As Long     '  Ox10     ' This is actually a String, but it's not directly accessible.  See FetchKey.
    pPrevIndexedItem    As Long     '  Ox14
    pNextIndexedItem    As Long     '  Ox18
    pvUnknown           As Long     '  Ox1C
    pParentItem         As Long     '  Ox20
    pRightBranch        As Long     '  Ox24
    pLeftBranch         As Long     '  Ox28
    bFlag               As Boolean  '  Ox2C
End Type                            '  Ox30 ' Length. (boolean padded to 4)

We can see that the VbCollectionHeader contains the Count of items in the collection, so it's anticipated that, today, Count is kept track of internally and thus behaves more like a property than a method.

Sancarn
  • 2,575
  • 20
  • 45