Yes, we all know this.
In this case, I am calling into a method on a wwBusiness class from Rick Strahl's West Wind framework.
Here's the code, for those who are curious. It's loaded with Return statements. It's not my work, so I have no choice but to call into it. And I'm certainly not going to change his source code. Sure, it has multiple Returns, however, they all seem properly coded and guarded to me. I'm not about to knock him. He's a master coder for sure, and today he might agree or do it differently, but this code has been around for years.
Procedure wwbusiness.Query
LPARAMETERS lcSelect, lcCursor, lnResultmode
LOCAL lnResult
lcSelect=IIF(EMPTY(lcSelect),THIS.cSQL,lcSelect)
lcCursor=IIF(EMPTY(lcCursor),THIS.cSQLCursor,lcCursor)
lnResultmode=IIF(EMPTY(lnResultmode),THIS.nResultmode,lnResultmode)
THIS.seterror()
IF !THIS.OPEN()
RETURN 0
ENDIF
IF EMPTY( lcSelect ) OR NOT VARTYPE( lcSelect ) = "C"
lcSelect = THIS.cSQL
ENDIF
IF EMPTY( lcSelect )
lcSelect = "*"
ENDIF
IF ATC(" FROM",lcSelect) = 0
IF THIS.nDataMode = 0
lcSelect = lcSelect + " FROM '" + ALLTRIM(THIS.cDataPath + THIS.cFileName) + "' "
ELSE
lcSelect = lcSelect + " FROM " + ALLTRIM(THIS.cFileName) + " "
ENDIF
ENDIF
IF NOT UPPER(lcSelect) = "SELECT "
lcSelect = "SELECT " + lcSelect
ENDIF
THIS.cSQL = lcSelect
DO CASE
CASE THIS.nDataMode = 0
IF ATC(" INTO",lcSelect) = 0
lcSelect = lcSelect + " INTO CURSOR " + lcCursor
ENDIF
&lcSelect
IF !USED(lcCursor)
RETURN 0
ELSE
lnResult = _TALLY
ENDIF
CASE THIS.nDataMode = 2
lcOldCursor = THIS.oSQL.cSQLCursor
THIS.osql.cSQLCursor = lcCursor
lnResult = THIS.osql.Execute(lcSelect)
IF lnResult < 0
THIS.seterror(THIS.osql.cErrorMsg)
RETURN 0
ENDIF
THIS.oSQL.cSQLCursor = lcOldCursor
lnResult = RECCOUNT()
CASE THIS.nDataMode = 4
lcOldCursor = THIS.oHTTPSQL.cSQLCursor
THIS.oHTTPsql.cSQLCursor = lcCursor
lnResult = THIS.oHTTPsql.Execute(lcSelect)
IF lnResult < 0
THIS.seterror(THIS.oHTTPsql.cErrorMsg)
RETURN 0
ENDIF
THIS.oHTTPSQL.cSQLCursor = lcOldCursor
ENDCASE
IF lnResultmode # 0
THIS.ConvertData(lnResultmode,,lcCursor)
USE IN (lcCursor)
ENDIF
RETURN lnResult
EndProc
>IMO multiple return/exit points are bad practice. I never, ever use them - so many Heisenbugs. If I have to work on someone else's code that has them, I refactor - no exceptions.
>
>It's off-topic, but if you get nothing else out of this thread, consider never having multiple exit points in your code.
>
>This topic has been heatedly debated here before and there's no need for me to get involved again - subscribers can search :)
>
>>Others have mentioned the possibility of various return point. There are multiple return points in the code, but I reviewed the code all Return points are properly coded to return an integer. Regardless, my new pattern would still be affected if a bool return type came back because of bad coding in the called method. I changed nothing in the baseclass code, but yet I began to get an int value back, like I was expecting.
>>
>>I agree, it should have worked tghe way it was... And today it does. I'm not sure what was wrong.
>>
>>I just know that I was testing for (lnReturn > 0) and lnReturn was actually a coming back as a bool, so my test code failed. The I stepped through the bebugger to see whay I was getting a bool, and I realized that I could not step into the Dodefault() code which was being called inline with a Return stament, therefore, I could not tell which part of the called code was returning the bool value. So, I assumed it was some oddity with callind DoDefault() inline with a Return statement.
>>
>>I changed to the new format, then I could step into the DoDefault() code, and it passed back an int, like it should have all along.
>>
>>Regardless, the mere fact that step-through debugging is affected by this inline pattern is enough to make me abandon it.
>>
>>
>>
>>
>>
>>
>>
>>>RETURN DODEFAULT() always works. I believe it's a good practice if you want your subclass code to run, then the default. Don't abondon -- fix what's wrong. One could argue that all sorts of combined statements make a harder to debug, and they do; but once you trust the pattern, it's not an issue. Perhaps you have mulitple RETURNS in a procedure?
>>>
>>>>Well, your code indeed works fine.
>>>>
>>>>And, I've gone back to my own code, and now I cannot get it to repeat the results I was getting last night.
>>>>
>>>>I *KNOW* for sure that is was not working correctly, but today I cannot replicate it.
>>>>
>>>>However, I still am resolved that I like my modified coding sequece anyway, so I'm still going to abandon the Return DoDefault() structure going forard.
>>>>
>>>>
>>>>
>>>>>>Some of you probably already knew this, but it caught me off guard...
>>>>>>
>>>>>
>>>>>Are you sure that's what's happening? Are you sure your base class always returns a number (and doesn't have some cases where it just does a RETURN out of the code)?
>>>>>
>>>>>I tried it and it works fine:
>>>>>
>>>>>
>>>>>o = CREATEOBJECT("TestB")
>>>>>?o.DoSomething("")
>>>>>
>>>>>DEFINE CLASS TestA AS Custom
>>>>> FUNCTION DoSomething(tcString)
>>>>> RETURN 1
>>>>> ENDFUNC
>>>>>ENDDEFINE
>>>>>
>>>>>DEFINE CLASS TestB AS TestA
>>>>> FUNCTION DoSomething(tcString)
>>>>> RETURN DODEFAULT(tcString)
>>>>> ENDFUNC
>>>>>ENDDEFINE
>>>>>