Private bInit As Boolean Private l As Long Public Property Let Value(ByVal v As Variant) If bInit Then Err.Raise 17 Else bInit = True End If If vbLong <> VarType(v) Then Err.Raise 13 Else l = CLng(v) End If End Property Public Property Get Value() As Variant Value = l End Property
Class ConstiLong instances are used as constant long integers. The Value property is marked as the default property for the class (in common with normal Smarties). You can see that the Property Let allows one-time-only initialization of the contained value (l). You can also see that I'm using a Variant to type check the argument in the Let (Visual Basic then insists on my using a Variant for the Get, too). You can remove these and use a real Long if you want (and I can guarantee that you'll be set from a Long).
From Chapter 1, here's how you'd set these up (all this code is in your start-up module).
' Used to extend sub-classed VarType for Smartie Types. ' Public vbObjectiInteger As New ConstiLong Public vbObjectiSingle As New ConstiLong Public vbObjectiString As New ConstiLong . . . Sub main() vbObjectiInteger = WinGlobalAddAtom(CreateGUID) vbObjectiSingle = WinGlobalAddAtom(CreateGUID) vbObjectiString = WinGlobalAddAtom(CreateGUID) . . . End Sub
Obviously, I need one of these classes for each intrinsic type from which I want to make a constant-ConstiInt, ConstiString, and so on. (If you're not sure what's going on, see my aside on Smarties in Chapter 1.)
Another thing you can use Smarties for is recording assigned values. This might sound a bit weird, but it is a useful thing to do with them. What do I mean? Consider this code:
Dim n As New iInteger n = SomeFunc(SomeParam) End Sub
Because iInteger is a Smartie, it can, of course, do all sorts of stuff when its default property Let Value gets hit-like record the value assigned from SomeFunc in the Registry. Remember code where you have stuff like this?
' Ask the user for some info... n = MsgBox("Show tips at startup?", vbYesNo + vbQuestion, _ "Show Tips at Startup") ' Write away to persistent store. Call SaveSetting(... n ...) If vbYes = n Then ...
With Smarties you can have the same thing happen with just this assignment:
' Ask the user for some info and record it away... n = MsgBox("Show tips at startup?", vbYesNo + vbQuestion, _ "Show Tips at Startup") If vbYes = n Then ...
Without proper construction (a parametizable constructor procedure for classes, also known as a declarative initialization), this assignment is of little real use. For example, how does this instance of iInteger know which key it should write to in the Registry? What you really need to support this kind of thing is declarative support, something like this-Dim n As New ipInteger(kTips). The ip here means intelligent-persistent iInteger (a class that would implement iInteger); kTips is a value that is passed to the created ipInteger instance, telling it which Registry value it should be writing to. In this scenario, it would probably write to App.EXEName\Settings\kTips. Currently the only way to parameterize the construction of objects such as this is by using public variables, such as the ones shown here.
kTips = "Tips" Dim n As New ipInteger: n = Nothing
The n = Nothing causes n's Initialize event to trigger and read the value of the public kTips, which is really nasty, prone to error, and basically awful!
Of course, this code hides the fact that the value in n is written somewhere, so you might consider this "clever code" and discard it. It's up to you-where cleverness starts and abstraction ends is somewhat subjective. For example, what does this code look like it might do?
Dim n As New iInteger n = n
Nothing right-the code simply assigns 0 to n. Well, that's what would happen if n were an ordinary integer, but with a Smartie we cannot be certain. As you probably know, "Dim-ing" an object using New doesn't create it-the object is created at its first use. So n is actually created when we read the value of n (because the right side of the assignment is evaluated first). This statement causes the object to be created and thus causes its Initialize event to fire. Hmm, it could be doing anything in there, like reading from the Registry and setting up its own value! Would you expect to find, if the next line read MsgBox n, that n contains, say, 42? Probably not. Of course, the code might look even more obvious:
n = 0 n = Nothing Set n = Nothing
Notice that n = Nothing is kinda odd (in more ways than one). The statement is really "the Value (default) property of n is assigned Nothing," or Call n.Value(Nothing), so the statement is perfectly legal and causes Nothing to be passed to our object as a parameter-and, of course, causes it to be created. Notice, too, that it's very different from Set n = Nothing; this statement doesn't invoke the Value property Set even though the syntax makes it appear that the default property is being accessed. To Set the Value property, of course, you need to use Set n.Value = Nothing-see the ambiguity here? (If a default were allowed for both regular assignment and Set, Visual Basic would have no way of knowing whether you wanted to set the default property or the object variable.) Actually, depending on whether n is declared Dim n As New iInteger or Dim n As iInteger (which is then set using Set n = New iInteger), even a Set n = Nothing might have no effect. A show of hands, anyone, who thinks that an object declared Dim o As New ObjectName can be set to Nothing using Set o = Nothing. That many? Think again!
You can also, of course, log more ordinary assignments using Smarties. Should you feel that some routine is periodically returning an interesting value, have the assigned-to Smartie log the assigned value to the Registry so that you can check it.
In my humble opinion, the nicest place to eat in all of Seattle (perhaps even the world, excluding The Waterside Inn in Bray in England) is The Brooklyn Seafood, Steak & Oyster House, on the intersection of Second and University in downtown. This place is just dazzling, and I have spent many a happy, self-indulgent evening there immersed in its glow and hubbub. One of my favorite things to do there is to sit at the chef's bar and watch the show on the other side of the bar. If they're makin' flames, so much the better. The chef's bar has great old-fashioned high-back swivel chairs stationed all along it for the sole purpose of allowing you to watch, and, if you're not careful, be a part of the drama taking place there. In fact, it's a lot like sitting in a theater, except it's much more intimate and generally a lot more fun!