Home
Foreword
Preface
Class Idioms
Collections
Implements
Constructors
Terminate
Forms
On Error
Frameworks
F.A.Q.
Value-added
FSMs
Constants
GOTO
Hungarian
Nothing
Properties
Big EXEs

When news first surfaced that VB4 was going to have class modules, programmers were ecstatic that at last they could use object-oriented techniques in their Visual Basic programs. They were soon disappointed. Chief among the criticisms that left C++ and Delphi programmers shaking their heads at VB's half-baked implementation of classes was the glaring omission of constructors. It took a while to sink in, because VB classes have promising-looking events called Initialize and Terminate, which seem to correspond to the the all-important constructor and destructor functions found in object-oriented languages. However, when the inevitable question of how to pass arguments to the Initialize event comes up, most are dismayed to discover that it isn't possible!

Elaborate schemes have been devised for faking real Constructors in Visual Basic, but their reliance on PublicNotCreatable objects and an open-ended number of ActiveX DLLs makes them impractical for day to day programming. We can, however, devise a very simple and much more practical scheme that telescopes the creation and initialisation of a class into a single statement.

Here's a typical object creation scenario in Visual Basic:

Dim oFruit As CFruit
Set oFruit = New CFruit
oFruit.Name = "Apple"
oFruit.Variety = "Braeburn"
oFruit.Size = 2

Aside from it verbosity, the main problem with this code is that we can easily omit part of it and leave the object in an inconsistent partly-initialised state. The traditional way to get around this in Visual Basic is to provide a method to do the initialisation:

Public Sub Create(ByVal sName As String, _
                  ByVal sVariety As String, _
                  ByVal nSize As Integer)
    Me.Name = sName
    Me.Variety = sVariety
    Me.Size = nSize
End Sub

There isn't much wrong with this except that it takes two statements to create a class instance, which makes our object creation code more verbose. It's also possible to omit the second statement (the call to Create), which results in an uninitialised object.

What we're after is a way to express our example as a single statement, in the manner available to C++ and Java programmers. Here's how such a statement would look if we expressed it in a VB-like syntax:

Set oFruit = New CFruit("Apple", "Braeburn", 2)

Let's have a look at two variations of a scheme that will give us this in Visual Basic. We can't quite get the syntax we want, but the first attempt is pretty close:

Set oFruit = Construct(New CFruit, "Apple", "Braeburn", 2)

'Construct' is a global function that we need to put in a BAS file or a Global class. It looks like this:

Public Function Construct(ByVal obj As Object, _
                 ParamArray Args() As Variant) As Object
    Dim vArgList As Variant
    vArgList = Args
    obj.Constructor vArgList
    Set Construct = obj
End Function

The CFruit class definition needs a method called 'Constructor', which looks like this:

Public Sub Constructor(ByVal Args() As Variant)
    Name = Args(0)
    Variety = Args(1)
    Size = Args(2)
End Sub

This is okay, but it has the disadvantages that we must provide a 'Construtor' method for each class, and that the parameter list isn't self-documenting. However, a variation lets us use 'named' parameters (sort of), resulting in the following code:

Dim oFruit As CFruit
Set oFruit = Construct(New CFruit, _
                       "Name", "Apple", _
                       "Variety", "Braeburn", _
                       "Size", 2)

This requires a more sophisticated version of the 'Construct' function, but it has the advantage that the CFruit class doesn't need any special methods. Here's the new global function:

Private Function Construct(ByVal obj As Object, _
                  ParamArray Args() As Variant) As Object
    Dim nArg As Integer
    For nArg = 0 To UBound(Args) Step 2
        CallByName obj, Args(nArg), _
                   IIf(IsObject(Args(nArg + 1)), _
                                VbSet, VbLet), _
                   Args(nArg + 1)
    Next nArg
    Set Construct = obj
End Function

This uses CallByName to invoke the property procedures by name (so it won't work with VB5), and we pass the arguments in pairs, using the first of each pair to give us a semblance of named arguments. Apart from the single global 'Constructor' function, it requires no extra code for our class definitions, removing both the need for a Create function and a line of code to call it!

We should note that this version of 'Construct' isn't completely general because it doesn't handle properties that take parameters. To handle this we'd need to extend the parameter list convention to include an argument count.
 

Key Spinner

© 1998 - 2009 Mark Hurst. All rights reserved.   Updated March 01, 2009