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. |