Sub Command1_Click () Debug.Print SomeFunc() Call SomeFunc End Sub Function SomeFunc () As Integer SomeFunc = 42 End Function
The line Call SomeFunc is illegal in Visual Basic 3 but legal in Visual Basic 4 and later. (It's VBA!) In case you're wondering why this is so, the facility was added to VBA (Visual Basic for Applications) to allow you to write routines that were more consistent with some of Visual Basic's own routines, such as MsgBox, which acts sometimes like a function and sometimes like a statement (or a C type procedure if you're used to that language). (In Tip 4, you'll find out how to write your own MsgBox routine.)
A side effect of all this is that routines that return some indication of success or failure might now have that result ignored. As C and SDK programmers know only too well, this will cause problems! In Visual Basic 3, the programmer always had to use the return value. Typically, he or she would use it correctly. If a programmer can ignore a routine's returned value (say it's not a database handle but a True/False value-that is, either it worked or it failed), however, he or she usually will ignore it.
Exceptions, on the other hand, cannot easily be ignored (except by using On Error Resume Next or On Error Resume 0-both easy to test for and legislate against). Also, keep in mind that "newer" Visual Basic developers sometimes lack the necessary self-discipline to use and test return values correctly. By raising exceptions, you force them to test and then to take some appropriate action in one place: the error handler.
Another reason to use exceptions is that not using them can cause your code to become more difficult to follow-all those (un)necessary conditional tests to see that things have worked correctly. This kind of scheme, in which you try some code and determine that it didn't work by catching a thrown exception, is pretty close to "structured exception handling" as used in C++ and Microsoft Windows NT. For more on structured exception handling, see the MSDN Library Help. (Select the Contents tab, and follow this path: Visual C++ Documentation; Reference; C/C++ Language and C++ Libraries; C++ Language Reference; Statements; Exception Handling; Structured Exception Handling.)
Here's an example of a structured exception handling type of scheme:
Private Sub SomeWhere() If a() Then . . . If b() Then . . . If c() Then . . . End If End If End If End Sub
This example is not too hard to figure out. But I'm sure you've seen far more complex examples of nesting conditionals, and you get the idea! Here's the same code using exceptions to signal errors in a, b, or c:
Private Sub SomeWhere() ' TRY On Error Goto ???? a() . . . b() . . . c() . . . ' CATCH ???? ' Handle exception here. End Sub
Can you see the flow any easier here? What's implied by the presence of the error handler is that to get to the call to b, a must function correctly. By losing the If, you're losing some plain readability, but you're also gaining some readability-the code is certainly less cluttered. Of course, sometimes code is clear just because you're used to it. Consider replacing b, for instance, with a call to Open. If you were to use the If…Then scheme to check for errors, you couldn't check for any errors in Open because you can't put conditional statements around a procedure. So it's easy for you to accept the fact that after Open is called, if an error occurs, the statement following Open will not run. It works the same with the b function. If an error occurs in the b function, the error routine rather than the statement that follows b will execute.
If you adopt this kind of error handling scheme, just make sure that you have projectwide collaboration on error codes and meanings. And by the way, if the functions a, b, and c already exist (as used previously with the If statements), we'll be using this "new" ability to ignore returned values to our advantage.
Once again, if a routine's returned value can be ignored, a programmer will probably ignore it!