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

Visual Basic's Const statement lets you replace magic numbers or text messages with symbolic names. This is like defining read-only variables, and it's a Good Thing because it makes your code more readable. You should use lots of Const statements. Or should you?

Using Const isn't quite like defining variables, because the scope rules don't work the same. Defining constants inside a function is okay, and defining them at the module level with Private scopes just like a variable too. But just try this in a class module:

Public Const eBadUserName As Long = 32767

Now VB doesn't want to play. Why is this statement illegal? Surely defining read-only variables as properties of a class would be a useful thing? Apparently not; the only place we can define public constants with this syntax is in BAS modules. This is anathema to modular programing, since even if we want to associate constants with a particular class we have to put them somewhere else.

There's a pretty obvious way around this, and one which applies to both VB4 and VB5 (unlike Enums, which I'll talk about later). We can create read-only properties in a class, and these act like constants that are unambiguously associated with that class. For example, here's a constant for an exception thrown by a class method:

Public Property Get eBadUserName() As Long
    eBadUserName = 32767
End Property

This works like 'Public Const eBadUserName...' would work if you could do it, and it lets us code things like this in exception handlers:

Select Case Err.Number
     Case MyObject.eBadUserName:
        ...
End Select

Unfortunately this isn't quite as good as it first appears, and the reason gives us an insight into why 'Public Const' isn't allowed in classes. The problem is that for this kind of code to work we have to have an INSTANCE of the class before we can refer to the constant. This means, for example, that the following code doesn't work:

Sub MySub

    On Error Goto ErrorBlock

    Dim MyFirstObj As New CMyFirstClass  ' <-- error here
    Dim MySecondObj As New CMySecondClass
    ...

Errorblock:
    Select Case Err.Number
        Case MyFirstObj.eThisError
            ...
        Case MySecondObj.eThatError ' Object variable not set
            ...
    End Select
End Sub

(You might need to think about this a bit before you see why it doesn't work. The problem is that the Select Case statement is going to evaluate the property of MySecondObj even if MySecondObj hasn't been created yet.)

If we're using forms instead of classes, there is fairly elegant (ish) way around this that leads to satisfactory code. It relies on the fact that Visual Basic provides a 'magic form' for each form class we define, and that, crucially, it gives such forms the same name as the class (see Chapter 1 for more discussion of this). This means we can recode the example above using forms instead of classes, and everything will work fine:

Sub MySub

    On Error Goto ErrorBlock

    Dim MyFirstObj As New CMyFirstForm  ' <-- error here
    Dim MySecondObj As New CMySecondForm
    ...

Errorblock:
    Select Case Err.Number
        Case CMyFirstForm.eThisError
            ...
        Case CMySecondForm.eThatError ' No problem!
            ...
    End Select
End Sub

Note the subtle difference between the original example and this one: the constants are qualified with the CLASS name, not the INSTANCE name. This very Java-like syntax works because VB creates instances of CMyFirstForm and CMySecondForm behind the scenes, and these are used solely to provide the constants.

We could almost do this with an ordinary class too, but first we'd need to create the global instance manually, and second, we couldn't name this instances with the same name as the class. By using forms, we're taking advantage of Visual Basic's peculiar 'magic form' feature, where it contrives to use the same name for a class (form) and an instance of the class. This is even better than Public Const would be, because we're clearly referring to something that relates to the class and not the instance. The only thing we need to watch out for is that we never actually load the form (it won't harm, but it doesn't make sense).

So should you do this in practice? Maybe. Often it's very useful to use a form instead of a class, particularly if your class is going to have a single form associated with it. If the class has no visual components, there may be an overhead in using a form (I haven't investigated this) but it will work just as well. The other obvious overhead, of course, is that you always have one more instance of your class than you really need. There are also some practical problems with using forms instead of classes, which usually crop up when you're doing more advanced things. For example, you won't be able to set the default property of a form, since it has one already, and you can't make a form public in an ActiveX component.

Returning to more conventional methods, Visual Basic 5 introduced Enumerated Types. Enums can only be numeric (and only Long integers), but we can define them inside classes. This is a good start, because now we define our original exception code inside the class:

Public Enum tExceptions
    eBadUserName = 32767
End Enum

On the surface this is really just an alternative way of doing the following line in a BAS file:

Public Const eBadUserName As Long = 32767

However, we're immediately better off because the definition is inside the class file. A bit of experimentation shows that we can also create more than one enum using the same member names, which is a clue to something even more powerful. In fact it turns out that although we canīt qualify an enum value with the name of the class in which itīs defined, we can qualify it with the name of the enum itself. Hence, if we base our enum names on the class names we can get a pretty natural-looking naming convention:

Public Enum tCMyClass1
    eBadUserName = 32767
End Enum

And in use:

Select Case Err.Number
     Case tCMyClass1.eBadUserName:
        ...
End Select
 

Key Spinner

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