ByVal/ByRef is part of the "contract" between the author of a function/sub/method and the programmer calling it. If you call a procedure where one or more parameters are ByVal, you know that the procedure will not alter your variable.
ByVal means that the procedure is not allowed to change the value. ByRef (the VB6 default) means that the procedure is allowed to change it.
While this is true, it is also false. It depends on how you interpret it.
A ByVal parameter is a
local copy of the passed parameter expression's value. As such, the
copy can indeed be modified within the called routine. The point is that the altered value does not have any affect on the calling routine's variable or expression passed.
So you
could say: "ByVal means you are free to modify the variable without causing side-effects in the calling code. ByRef means you cannot change it or else you
do risk side-effects."
It's also true of course that people can get wrapped around the axle when they consider object references. The trick is to realize that you do indeed pass
object references and not
objects to a called procedure.
Thinking of the
reference being passed ByVal/ByRef makes it all clearer: a ByVal reference doesn't keep the procedure from modifying the referenced object, you merely can't change the value of the reference in the calling program's variable. With a ByRef object reference parameter however, the called routine can indeed "point" the caller's reference to some other object.
In most cases then, object references should be passed ByVal, just as with most data. In general you should use ByRef when the developer of the function/sub
intended that the routine modify one or more passed parameters. The "efficiency" issue only arises when passing long strings or arrays.