VFP学习、开发漫谈 (14)
今天,与大伙聊一聊有关自定义函数和过程问题。在 VFP 中,程序以函数定义或以过程(子程序)定义,二者在功能上没有任何区别,区别仅在于其被调用的形式和参数的默认调用方式。这是我反复斟酌之后得出的结论,不知道高手们怎么看。
默认情况下(SET UDFPARMS TO VALUE,调用时不在变量前加 @、不将变量放在括号内),以函数方式调用时,参数采用传值方式;以过程方式调用(DO 过程 WITH 参数)时,参数采用传地址方式。
以自定义函数 UDF_PLUS(a,b) 为例:
= UDF_PLUS(a,b) && 以函数方式调用,参数 a,b 采用传值方式
DO UDF_PLUS WITH a,b && 以过程方式调用,参数 a,b 采用传地址方式
通过将参数放在括号内,强制采取传值方式调用:
= UDF_PLUS((a),(b))
DO UDF_PLUS WITH (a),(b)
通过在参数前加 @,强制采取传地址方式调用:
= UDF_PlUS(@a,@b)
DO UDF_PLUS WITH @a,@b
通过 Set UDFPARMS To Value/Refrence 命令,可以修改调用自定义函数时,参数的默认传递方式。注意:该设置不影响以过程方式,即 DO 命令方式调用函数。例如 :
SET UDFPARMS TO VALUE
= UDF_PLUS(a,b) && 参数 a,b 采用传值方式
DO UDF_PLUS WITH a,b && 参数 a,b 仍然采用传地址方式
SET UDFPARMS TO REFRENCE
= UDF_PLUS(a,b) && 参数 a,b 采用传地址方式
DO UDF_PLUS WITH a,b && 参数 a,b 采用传地址方式
从上面的例子可以看出:在采用过程方式调用函数时,若要求参数以传值方式调用,必须采用强制方式。即:DO UDF_Plus WITH (a),(b)
当过程/函数所接收的参数多于所需要的个数时,VFP 会报错。例如:函数仅定义了两个形参,调用时却指定了 3 个实参,如下所示:
? UDF_Plus(1,2,3) && 以 3 个实参调用函数,则报错
FUNCTION UDF_PLUS(tnValue1,tnValue2) && 定义了 2 个形参
RETURN tnValue1+tnValue2
ENDFUNC
但如果过程/函数接收的参数个数少于所需要的数目,则 VFP 仅将余下的参数赋初值 .F.,而不报错。如下所示:
? UDF_PLUS(1) && 以 1 个实参调用函数,不报错
FUNCTION UDF_PLUS(tnValue1,tnValue2) && 定义了 2 个形参
*……
ENDFUNC
在上面的实例中,调用函数时仅指定了第 1 个参数,也可以仅指定第 2 个参数,如:? UDF_PLUS( ,1)
需要指出的是:使用 DO 命令调用函数或过程时,无法获取返回值,而使用函数方式调用时,既可以获取返回值,如:nRet=UDF_Plus(1,2),也可以仅执行函数或过程的代码而忽略返回值,如:= UDF_Plus(1,2)。因此,以函数方式调用子程序或函数更加灵活。我在开发应用程序时,所有函数或过程均使用 FUNCTION 来定义,同时均使用函数方式来调用。
在自定义函数中,使用 RETURN 命令来返回结果值。若没有 RETURN 语句,则函数自动返回 .t.
自定义函数仅能返回一个值。如果函数要返回多个值,该怎么办呢?在实际应用中,我主要采用以下两种方法。
第一种方法:将多个值组合成一个字符串,并返回该字符串。使用时,再将返回的字符串拆分开。这类似于对文件的压缩和解压缩。
程序代码:
LOCAL cRet,nPlus,nMinus cRet = Math(1,2) && 求 2 个数的“和”与“差” nPlus = VAL(STREXTRACT(cRet,'[',']',1)) && 获取“和” nMinus = VAL(STREXTRACT(cRet,'[',']',2)) && 获取“差” ? nPlus ? nMinus FUNCTION Math(tnValue1,tnValue2) LOCAL nPlus,nMinus,cRet nPlus = tnValue1 + tnValue2 nMinus = tnValue1 - tnValue2 cRet = '['+TRAN(nPlus)+']['+TRAN(nMinus)+']' RETURN cRet ENDFUNC
第二种方法:将要返回的多个值保存到变量中,并以传地址方式调用并返回。仍以上例来说明:
程序代码:
LOCAL nPlus,nMinus = Math(1,2,@nPlus,@nMinus) && 求 2 个数的“和”与“差” ? nPlus ? nMinus FUNCTION Math(tnValue1,tnValue2,tnPlus,tnMinus) tnPlus = tnValue1 + tnValue2 tnMinus = tnValue1 - tnValue2 ENDFUNC
当要返回多个值时,我更喜欢使用第二种方法。除了上述方法外,还可以采用全局变量或私有变量的方法,但从结构化编程的思路来说,每个函数都应该相对独立,其与外部程序的联系仅能通过参数来进行。因此,不建议采用该方法。
可以使用 PARAMETERS()返回过程或自定义函数的实参数目。这里的过程或自定义函数是指最近调用的过程/函数,并不是指当前过程/函数。
请看表单初始化事件中的一个错误:
程序代码:
LPARAMETERS tcPara IF CheckRight('cb_clmx') # '写' && 若当前用户无写权限,禁用修改按钮 THIS.cmdOK.Enabled = .f. ENDIF IF PARAMETERS() > 0 && 有参数传递时,定位记录 THIS.Locate(tcPara) ENDIF
当不传递参数调用表单时,tcPara=.f.,若在 Init 事件的第 2 行调用 PARAMETERS()则返回 0。但程序接着调用了 CheckRight(),此时 PARAMETERS()将返回 CheckRight()中的实参个数 1,导致语句 THIS.Locate(tcPara) 被执行,因而出错。
改正如下:
程序代码:
LPARAMETERS tcPara IF PARAMETERS() > 0 THIS.Locate(tcPara) ENDIF IF CheckRight('cb_clmx') # '写' THIS.cmdOK.Enabled = .f. ENDIF
或:
程序代码:
LPARAMETERS tcPara IF CheckRight('cb_clmx') # '写' THIS.cmdOK.Enabled = .f. ENDIF IF VARTYPE(tcPara) = 'C' THIS.Locate(tcPara) ENDIF
[ 本帖最后由 liuxingang28 于 2014-4-4 08:16 编辑 ]