See also: PassByReference and AlternativesToPassByReference.
Mostly VisualBasic
I had to wrestle with the following code:
Private Sub cmdView_Click() ShowPage m_url End Sub ... Private Sub ShowPage(url As String) url = "http://" & url ... code to bring up a browser and point it to url omitted... End SubThe first time you click <view> you go to http://www.foo.com, the second time the browser attempts to open http://http://www.foo.com and complains.
The implicit ByRef is to blame. The ShowPage routine should have been written
Private Sub ShowPage(ByVal url As String)Though some (including me) would argue that altering the value of an argument is BadPractice?.
(I second that notion. -- JeffGrigg)
That is...
Private Sub ShowPage(ByVal hostName As String) Dim url as String url = "http://" & hostName ...etc... End Sub....
While working on VisualBasic code, I routinely add explicit ByRef and ByVal prefixes to all parameters, so that none remain implicitly ByRef. Most VisualBasic code does not change parameter values, in spite of the parameters defaulting to ByRef, but showing one's intention in the interface reduces the chance of error.
Being an experienced CeePlusPlus programmer, I'd like to pass "const" objects between VisualBasic functions, but ByVal isn't it. So I have to use ByRef and just document the "constness" (or not) of my object usage.
-- JeffGrigg
In LanguageOrientedProgramming a ByRef in itself is not considered harmful, if it is used correctly. But in this case it is an LopCrime?, because nothing in the function name tells us about this hidden change. In the ThelopLanguage there are special words that must be used to indicate such behaviour. It would not even be acceptable, if the function would be made "tolerant"
Private Sub ShowPage(url As String) if Left$(url,7)<>"http://" then url = "http://" & url end if ... code to bring up a browser and point it to url omitted... End Subeliminating the problem shown above in the second call.
This version
Private Sub ShowPage(url As String) String url_complete url_complete=url if Left$(url_complete,7)<>"http://" then url_complete = "http://" & url_complete end if ... code to bring up a browser and point it to url_complete omitted... End Subwould also eliminate the quiet change of the parameter, but it is still unacceptable with respect to its name. Without having enough knowledge about the complete API environment I would suggest that the name should contain the words "Browser", "Url", "Show" or "Open", and optionally "Window", perhaps
Private Sub UrlOpenBrowserWindow(url As String) String url_complete url_complete =UrlRetUrlComplete(url) ... code to bring up a browser and point it to url_complete omitted... End Subor
Private Sub BrowserOpenUrlWindow(url As String) String url_complete url_complete =UrlRetUrlComplete(url) ... code to bring up a browser and point it to url_complete omitted... End Subif we assume that we want to deal with only one external default browser.
Isn't the 'Url' in the method name redundant? The parameter name explicitly tells it expects a url. -- ThomasEyde
Agreed. I would write something like the following:
Private Function PrependHttp(PartialUrl As String) As String PrependHttp = "http://" & PartialUrl End Function Private Sub OpenBrowserWindow(PartialUrl As String) String CompleteUrl CompleteUrl = PrependHttp(PartialUrl) ... code to bring up a browser and point it to CompleteUrl End FunctionThis also avoids mixing CamelCase with UnderscoreCase?, uses NounsAndVerbs in suitable places. I'd also try to remove the temp CompletedUrl if possible. -- MattRyall
See! ThingsWeHateAboutVbClassic, VbTeachesBadHabits, VbIsBadForNewbies, VbIsGoodForCrapProgrammers? (Choose your favorite! ;-)
There are additional confusions when VB ByRef and ByVal concepts are applied to objects:
In VB, many common kinds of objects that have default value properties and can be used directly as if they were those properties. For instance, a = Me.txtName does the same thing as Me.txtName.Value because (Value) is the default property for the class of Access.Control. When assigning object references, the Set keyword is used, so that case is unambiguous, but...
When a procedure accepts a parameter of Variant type, if an object instance supplied, the object is passed, and there's no convention to force the value to be used instead. This usually produces no symptoms, but let's say I'm adding a bunch of field values to a collection for use later, and then move to a new row in a recordset. If I say colValues.Add rst!FooField, I've just added the field, not the value, and when I move to a new row, the field's value changes! This is a common class of coding error in VB.
When writing custom functions, other behaviors of VB lead us to believe that we can solve the problem in the case of a Variant parameter by using ByVal. After all, if I try to pass 3 to a string argument by reference, I get an error, but if I pass by value, type conversion is automatically performed. Furthermore, an object reference is a reference, right? So ByVal as opposed to ByRef should force resolution of an object instance's default Value property, right? No, wrong.
What actually happens when you pass an object variable by value to a procedure that takes a Variant argument is simply that if the called procedure assigns a new reference to the parameter, that doesn't affect what object the passed variable now refers to after the procedure returns. If the parameter were ByRef, then assigning a new object instance within the called procedure would also change the object pointed to by parameter variable in the caller.
Oh, and by the way, if you pass an object member as a parameter to a procedure (e.g. Foo(fld.Value) ), it always acts as ByVal even if you explicitly said and intended ByRef (in this case, meaning the calling procedure can't assign a value to fld.Value by assigning to the parameter). This is because ByRef is implemented using pointers, but object properties are always retrieved behind the scenes using getter procedures, even if the members are defined as something like Public FooMember As String. Consequently, the reference that is passed to the procedure is always to the hidden, temporary variable that holds the value returned form the getter, not the the storage space of the member variable.
Mostly CeeLanguage/CeePlusPlus
In CeeLanguage/CeePlusPlus use HungarianNotation and prefix pointers (references) with a "p" ("r"). Balance *'s with p's and you're good. -- SunirShah
In C++, use const and you don't have to worry about HungarianNotation. In any case, there is usually no advantage in reusing a variable as shown above. You are allocating new memory in either case, why not just create a new variable name? -- WayneMack
To be fair, CeeLanguage has long had this problem with char * strings, and C does not have the option of choosing ByRef or ByVal. It is only by convention that you agree not to modify the string.
True, so true. Tho' it's nice to have const now.
JavaProgrammers would argue that const doesn't give guarantees. It is only by convention that you don't cast it away.
The ConstQualifier (in C) must be explicitly cast away (CastingAwayConst). This forces you to think about the cast, and decide whether you are doing the right thing or not. In CeeLanguage, const can never give cast-iron guarantees. In JavaLanguage it could, but you don't need it because you can get the same result with JavaInterfaces.