Chugging right along, we're almost ready to dive in to our real project itself! The last REALbasic theory topic I want to cover before we tackle the real problem at hand is shared methods.
If you look at the declare we're focusing on (DialogBoxIndirect), you'll notice that one of the parameters it takes is a DLOGPROC, which is a callback function used to communicate what's happening in the dialog box itself. For instance, if a user clicks on a button, you'll get a WM_COMMAND message telling you about it. Things of that nature are the backbone for firing off events that tell you what's happening.
You may be wondering what this has to do with shared methods. You've probably figured out that we're going to be using a shared method to implement this callback function, but why? To truly understand why, you're going to have to learn a few things about what shared methods are and what they mean to you as a programmer.
A shared method is a method that lives on a class, not a class instance. Breath deep, sit down, and internalize that statement. It lives on the class. Not a class instance. That means it's akin to a constant -- you can call a shared method without needing an actual class instance. Quick example time! Make a new class and name it Foobar. Then add a shared method by going to Project->Add->Shared Method. Your class should look something like this:
Class Foobar
Shared Sub Test()
MsgBox "Foobar test!"
End Sub
End Class
Now, to test this out, go to Window1.Open, and add the code Foobar.Test and run the project. You'll notice that a message box appears when the window opens, which is exactly what you'd expect. Notice that at no point did we need to call "new Foobar" in order to use the method called Test. That's because Test is a shared method!
Shared methods have a "draw-back" to them in that they cannot access a me or self keyword. If you think about it, this makes sense. Me and self refer to instances of a class, and the method is not called on a class instance. That means there's nothing which me or self could possibly refer to. At first blush, this may seem like not such a draw-back. But it also implies that you can't use any non-shared information contained within the class. That means no class properties, or class methods, no events. Basically, the only thing a shared method can refer to is other shared methods, shared properties and constants. Everything requiring an instance is off-limits (unless you happen to have an instance reference handy). This isn't a design limitation -- it's simply the way things have to work.
Back to the topic at hand -- why do we need a shared method for our callback? Simple! Because there's no me or self references allowed, that means that the method has no extra, hidden parameters. You see, the way class methods work is that there's a hidden parameter which comes before any of your parameters which references the self pointer. If we tried to use a class method for our callback, then the implicit first parameter would be totally hosed and things would crash horribly. But since shared methods don't have that implicit first parameter, they're safe to use. Ta da! Instant encapsulation. You see, you *could* always use a module method for this. But that would mean you'd have to move some logic for the callback into the module. This way you can keep all of the logic within the class itself so it's only in one location.
The last thing I want to touch on is how to make the encapsulation complete. Let's say you really do need access to a particular instance within the shared method. How would you accomplish that? If you recall, I said you'd need a class instance handy in order to do that -- and that's the trick. Many callbacks allow you to specify a cookie parameter. You can make use of that as a way to pass in a class instance or something allowing you to identify one so that you'll have the reference handy. Since you can't pass an object reference as a parameter to the declare (it's not like any declare would know what to do with a REALbasic class reference!), you have to get slightly creative. The usual approach is for the class to have a shared property of type Dictionary, which can then have class instances stored and retrieved from it. The method calling the declare would put itself into the dictionary, and the callback could pull it back out (this allows you to have multiple class instances which share the callback and property -- if you only stored one reference, each instance would stomp on the other one). The only thing to watch out with this approach is that you remove the class reference from the dictionary when appropriate. Failing to do so will leak your class.
That should wrap up the theory we need to cover. Tune in next time where we start implementing our ultimate goal by making use of the topics we've been covering the last few days.
Leave a comment