Another common excuse for using ByRef is the argument of speed: passing by reference is a few milliseconds faster than passing by value because Visual Basic has to create a copy of the variable when it's passed by value. But the consequence of misusing ByRef can be severe in terms of debugging time. Imagine a seldom-used application configuration variable that gets inadvertently changed by another procedure. You might not detect the error until someone uses the configuration function several times-maybe even long after you've written the code. Now imagine trying to trace the cause of the problem! As a rule, always pass parameters by value unless you explicitly want changes to be passed back to the caller.
The purposes of passing parameters to a procedure rather than using module-level variables are to make it obvious to anyone not familiar with the code exactly what external dependencies are being used, and to allow the procedure to be rewritten or reused more easily. A good practice is to document procedure parameters in a header box. A header box is simply a series of comments at the beginning of a procedure that explain the purpose of the procedure. Any external dependencies should also be documented here. Often programmers do not reuse functionality simply because the parameters or dependencies are unclear or not easy to understand. Imagine a procedure that accepts an array containing 20 data items. If the procedure is dependent on all 20 data items being present, other programmers might find it difficult to use unless it is well documented.
Passing parameters to procedures allows you to create code that is loosely coupled and therefore potentially reusable. The following code fragment shows a would-be reusable procedure that is too tightly coupled to the form it's in to be reused anywhere else in that application, let alone in another application:
Sub SearchForFile(ByVal isFile As String) ' Disable all buttons. cmdClose.Enabled = False cmdView.Enabled = False ' Process . . . labStatus = "File " & isFile . . .
The procedure is rewritten here in a more reusable way:
Sub cmdProcess_Click() Dim ctlDisableArray(0 To 1) As Control Dim sFile As String sFile = filename ctlDisableArray(0) = cmdClose ctlDisableArray(1) = cmdView Call SearchForFile(sFile, ctlDisableArray(), labStatus) . . . End Sub Sub SearchForFile(ByVal isFile As String, _ Optional ctlDisable() As Control, _ Optional labUpdate As Label) Dim nIndex As Integer ' Disable all buttons if any are specified. If Not IsMissing(ctlDisable) Then For nIndex = LBound(ctlDisable) To UBound(ctlDisable) ctlDisable(nIndex).Enabled = False Next nIndex End If ' Process . . . If Not IsMissing(labUpdate) Then labUpdate = "File " & isFile End If . . .
Now the procedure is totally decoupled and can be called from anywhere in the application.