第八章 疯狂Caché 调用自定义代码模块(一)
本章介绍如何创建和调用用户定义的ObjectScript代码模块。这些代码单元可以是用户定义的函数、过程、方法、例程或子例程。
与在其他语言中一样,ObjectScript允许创建可以直接调用的命名代码块。这样的块称为过程。严格地说,在ObjectScript中,作为过程的代码块具有特定的语法和结构。
过程定义的语法如下:
ProcedureName(Parameters) [PublicVariables]
{
/* code goes here */
QUIT ReturnValue
}
该过程的元素(此处称为ProcedureName)包括:
- 参数(零个或更多) 它们可以是任何类型,并且与ObjectScript的典型情况一样,在定义过程时不需要声明它们的类型。默认情况下,它们是通过值(而不是通过引用)传递的。除非另有说明,否则它们的作用域是过程的局部作用域。
- 对公共变量的引用(零个或更多) 这些也可以是任何类型的。该过程可以引用和设置此类变量的值。
- 声明该过程是公共的(可选) 默认情况下,过程是私有的,这意味着只能从同一例程的其他位置调用它们(在ObjectScript术语中,例程是包含一个或多个过程Procedures或其他用户定义的代码块的文件)。还可以创建公共的过程,在过程名称后使用PUBLIC关键字。可以从其他例程或方法调用公共过程。
- 代码 过程中的代码具有ObjectScript中提供的所有功能。过程代码还可以包括BASIC和Java。代码由大括号分隔,也称为过程块。
- 返回值(可选) 这是该过程返回的值,并且必须是标准ObjectScript表达式。过程内的流程控制可以使用计算表达式值和/或多个QUIT语句指定各种返回值。
注意:对于熟悉版本5之前的Caché版本(以及为这些版本编写的代码)的人来说,Procedures过程比以前在子例程和用户定义函数中提供的编码更先进。过程参数在过程内的作用域中自动是局部的。它们不需要NEW命令来确保它们不会覆盖其他值,因为它们是过程专用的,并且不与符号表交互。此外,公共变量的显式声明允许引用应用程序中的全局变量,例如银行范围的利率;它还允许为过程中的变量创建和设置可供应用程序其余部分使用的值。
Procedures过程是一种特殊的ObjectScript例程。
Caché还提供了大量系统提供的函数,所有这些函数都在ObjectScript语言参考中进行了描述;这些函数有时称为内部函数。对系统函数的调用由“$
”前缀标识。
Procedures, Routines, Subroutines, Functions, Methods 他们是什么?
本章介绍如何使用过程实现自己的代码,这些过程是实现用户定义功能的推荐形式。虽然Procedures, Routines, Subroutines, Functions, Methods这些实体都有共同的功能,但每个实体都有自己的特点。最值得注意的是,在Caché5.0版本之前创建的应用程序通常包括未实现为过程.res的子例程和函数,每个子例程和函数都有自己的特征。因此,为实现自己的代码提供所有这些不同结构的简要概述是很重要的。
最灵活、最强大、最推荐的命名用户定义代码块形式是过程。程序的特点包括:
- 可以是私有的,也可以是公共的。
- 可以接受零个或多个参数。
- 自动将在其中创建的任何变量维护为作用域中的局部变量。
- 可以引用和更改它外部的变量。
- 可以返回任何类型的值,也可以不返回值。
重要提示:默认情况下,方法是过程。正因为如此,本章中关于过程的所有内容也都描述了方法。
相比之下:
- 子例程始终是公共的,不能返回值。
- 函数始终是公共的,需要显式声明局部变量(否则将覆盖外部变量),并且必须具有返回值。
- 默认情况下,方法是作为类定义的一部分指定的过程,可以在一个或多个对象或类上调用该过程。如果显式声明它是一个函数,那么它就是一个具有所有附带特征的函数;不建议这样做。
- 例程是ObjectScript程序。它可以包括一个或多个过程、子例程和函数,以及三者的任意组合。
ObjectScript还通过其宏工具支持相关形式的用户定义代码。
Routines
例程是一个可调用的用户编写的代码块,它是一个ObjectScript程序。例程执行通常需要的操作。其名称由选择用于保存它的.Mac文件的名称确定。根据例程是否返回值,可以使用以下一组语法或两组语法调用例程:
DO ^RoutineName
SET x = $$^RoutineName
w 1,"!"
w 2,"!"
s white="白色"
w white,!
DHC-APP>D ^PHA.MOB.TEST
1!2!白色
例程在命名空间内定义。可以使用扩展例程引用来执行在当前命名空间以外的命名空间中定义的用户定义例程:
DO ^|"SAMPLES"|RoutineName
通常,例程充当子例程、方法和过程的容器。
此标签(通常)后跟括号,其中包含要从调用程序传递到例程的参数列表。
将例程保存到文件时,文件名不能包含下划线(“_
”)、连字符(“-
”)或分号(“;
”)字符;包含此类字符的名称无效。
Subroutines
子例程是例程内的命名代码块。通常,子例程以LABEL开头,以QUIT
语句结束,它可以接受参数,不返回值。要调用子例程,请使用以下语法:
DO Subroutine^Routine
其中,子例程是例程文件(Routine.MAC)中的代码块。
子例程的形式为:
Label(parameters) // comment
// code
QUIT // note that QUIT has no arguments
如果将代码和QUIT
语句用大括号括起来,则该子例程是一个过程,可以这样处理。
Functions
Caché附带许多系统提供的函数(有时称为“内部intrinsic”函数).本节介绍用户定义的(“外部extrinsic”)函数。
函数是例程中的命名代码块。通常,函数以LABEL开头,以QUIT
语句结束。它可以接受参数,也可以返回值。要调用函数,有两种有效的语法形式:
SET rval=$$Function() /* 返回值 */
DO Function^Routine /* 忽略返回值 */
其中函数是例程文件(Routine.MAC)中的代码块。在这两种语法形式中,都可以使用扩展例程引用来执行位于不同名称空间中的函数。
函数的形式为:
Label(parameters)
// code
QUIT ReturnValue
如果将代码和QUIT
语句用大括号括起来,则该函数是一个过程,可以这样处理。请注意,因为默认情况下过程是私有的,所以可能希望指定PUBLIC关键字,如下所示:
Label(parameters) PUBLIC {
// code
QUIT ReturnValue }
如果方法是私有的你调用的话
Main PRIVATE ;
TRY {
KILL x
SET x=$$MyFunc(7,10)
s y = $$Call
d Start
WRITE "returned value is ",x,!
RETURN
}
CATCH { WRITE $ZERROR,!
}
MyFunc(a,b)
SET c=a+b
QUIT c
/// d ##class(PHA.TEST.ObjectScript).TestRoute()
ClassMethod TestRoute()
{
d Main^PHA.MOB.TEST
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestRoute()
d Main^PHA.MOB.TEST }
^
zTestRoute+1^PHA.TEST.ObjectScript.1
下面的示例定义一个简单函数(MyFunc)并调用它,传递两个参数并接收返回值:main
;
Main ;
TRY {
KILL x
SET x=$$MyFunc(7,10)
WRITE "returned value is ",x,!
RETURN
}
CATCH { WRITE $ZERROR,!
}
MyFunc(a,b)
SET c=a+b
QUIT c
DHC-APP>D Main^PHA.MOB.TEST
returned value is 17
调用函数的代码可以忽略返回值,但函数的Quit
命令必须指定返回值。尝试使用无参数退出退出函数会生成
错误。
错误指定调用函数的调用位置,后跟一条消息,指定无参数退出命令在被调用函数中的偏移位置。
定义程序
与在其他语言中一样,过程是完成特定任务的一系列ObjectScript命令(较大例程的一部分)。与IF
等构造类似,过程的代码包含在大括号中。
过程允许将每个变量定义为公共变量或私有变量。例如,以下过程称为“MyProc
”:
MyProc(x,y) [a,b] PUBLIC {
Write "x + y = ", x + y
}
定义一个名为“MyProc”的公共过程,该过程接受x
和y
两个参数。它定义两个公共变量a
和b
。该过程中使用的所有其他变量(在本例中为x
和y
)都是私有变量。
默认情况下,过程是私有的,这意味着只能从同一例程中的其他地方调用它们。还可以创建公共的过程,在过程名称后使用Public
关键字。可以从其他例程调用公共过程。
过程不需要定义参数。要创建带有参数的过程,请将带括号的变量列表紧跟在标签之后。
调用程序
要调用过程,要么发出指定该过程的do
命令,要么使用“$$
”语法将其作为函数调用。可以控制是从任何程序(公共)调用过程,还是只能从其所在的程序(私有)调用过程。如果用DO
调用,过程不返回值;如果作为函数调用调用,过程返回值。“$$
”表单提供的功能最多,通常是首选表单。
使用$$
前缀
可以在允许表达式的任何上下文中调用用户定义函数。用户定义的函数调用采用以下形式:
$$name([param[ ,...]])
- name 指定函数的名称。根据函数的定义位置,名称可以指定为:
- label 是当前例程内的行标签
- LABEL^ROUTINE是驻留在磁盘上的命名例程中的行标签。
- ^routine是驻留在磁盘上的例程。例程必须仅包含要执行的函数的代码。
- param 指定要传递给函数的值。提供的参数称为实际参数列表。它们必须与为函数定义的形参列表匹配。例如,函数代码可能需要两个参数,第一个是数值,第二个是字符串文字。如果为第一个参数指定字符串文字,为第二个参数指定数值,则函数可能会生成不正确的值或可能生成错误。形参列表中的参数总是由函数调用
new
。参数可以通过值传递,也可以通过引用传递。如果传递给函数的参数比函数的正式参数列表中列出的参数少,则使用参数默认值(如果已定义);如果没有默认值,则这些参数保持未定义状态。
使用DO
命令
可以使用do
命令调用用户定义的函数。(不能使用do
命令调用系统提供的函数。) 使用DO
调用的函数不返回值。也就是说,函数必须生成返回值,但do
命令会忽略该返回值。这极大地限制了使用DO
调用用户定义的函数。
要使用DO
调用用户定义的函数,请使用以下语法发出命令:
DO label(param[,...])
do
命令调用名为Label的函数,并向其传递由param指定的参数。请注意,不使用$$
前缀,参数括号是必需的。 指定标签和参数的规则与使用$$
前缀调用用户定义函数时的规则相同。
函数必须始终返回值。但是,当使用DO
调用函数时,调用程序会忽略此返回值。
程序语法
label([param[=default]][,...]) [[pubvar[,...]]] [access] {
code
}
调用语法:
DO label([param[,...]])
和
command $$label([param][,...])
- label 程序名称。一个标准的标签。它必须从第一列开始。标签后面的参数括号是必需的。
- param 程序的每个参数的变量。这些参数称为形式参数列表。参数本身是可选的(可能没有、可能有一个或多个参数),但括号是必需的。多个参数值用逗号分隔。参数可以按值或按引用传递到形参列表。作为例程的过程不包含有关其参数的类型信息;作为方法的过程包含此信息。形参的最大数量为255个。
- default 它前面的参数的可选默认值。可以为每个参数提供或省略默认值。当没有为该形参提供实际参数时,或者当通过引用传递实际参数并且所讨论的局部变量没有值时,将应用默认值。此默认值必须是文字:可以是数字,也可以是用引号括起来的字符串。可以指定空字符串(
“”
)作为默认值。这与未指定默认值不同,因为空字符串定义变量,而未指定或未指定默认值的参数的变量将保持未定义状态。如果指定的默认值不是文字,则Caché会发出<参数>
错误。 - pubvar 公共变量。程序使用并可用于其他例程和程序的公共变量的可选列表。这是一个变量列表,这些变量既在此程序中定义,也可用于其他例程,并在另一个例程中定义,可用于此过程。如果指定,则将pubvar括在方括号中。如果未指定pubvar,则可以省略方括号。多个pubvar值用逗号分隔。所有未声明为公共变量的变量都是私有变量。私有变量仅对过程的当前调用可用。它们在调用过程时未定义,在退出过程时销毁。如果该过程调用该过程外部的任何代码,则私有变量将被保留,但在调用返回到该过程之前不可用。所有
%
变量始终是公共的,无论它们是否在此处列出。公共变量列表可以包括为此例程指定的一个或多个参数。 - access 一个可选关键字,用于声明过程是公共的还是私有的。有两个可用值:
public
,它声明可以从任何例程调用此过程。Private
,它声明此过程只能从定义它的例程中调用。私有是默认设置。 - code 一段代码,用大括号括起来。左花括号(
{
)必须与其前后的字符至少用一个空格或换行符分隔。右大括号(}
)后面不能跟同一行的任何代码;它后面只能跟空格或注释。右大括号可以放在第一列。此代码块仅通过标签输入。
不能在命令及其参数之间插入换行符。
每个程序都作为例程的一部分实现;每个例程可以包含多个程序。
除了标准的ObjectScript语法之外,还有管理例程的特殊规则。例程中的一行可以在开头有标签(也称为标记)、ObjectScript代码和结尾的注释;但所有这些元素都是可选的。
InterSystems建议例程的第一行有一个与例程名称匹配的标签,后跟一个制表符或空格,后跟一个解释例程目的的简短注释。如果行有标签,则必须用制表符或空格将其与行的其余部分隔开。这意味着当使用Studio向例程添加行时,可以键入标签和制表符/空格,然后键入ObjectScript代码,或者跳过标签并键入制表符或空格,然后键入ObjectScript。因此,在任何一种情况下,每行都必须在第一个命令之前有制表符或空格。
要表示单行注释,请使用双斜杠(“//
”)或分号(“;
”)。如果注释跟在代码后面,则斜杠或分号之前必须有空格;如果该行只包含注释,则斜杠或分号之前必须有制表符或空格。根据定义,单行注释中不能有换行符;对于多行注释,注释的开头用“/*”标记,注释的结尾用“*/”
标记。
程序变量
程序和方法都支持私有变量和公共变量;以下所有语句都同样适用于程序和方法:
程序中使用的变量自动成为该程序的私有变量。因此,不必这样声明它们,它们也不需要New
命令。要与此过程调用的过程共享其中一些变量,请将它们作为参数传递给其他过程。
还可以声明公共变量。它们可用于所有过程和方法;即此过程或方法调用的过程和方法以及调用此过程或方法的过程和方法。应该以这种方式定义数量相对较少的变量,以充当应用程序的环境变量。要定义公共变量,请将它们列在过程名称及其参数后面的方括号中。
publicvarsexample
DO proc1(4,5)
QUIT
proc1(e,f) [a, b]
{
WRITE !, "setting a" SET a = 10
WRITE !, "setting b" SET b = 20
WRITE !, "setting c" SET c = 30
SET d = a + b + c
w !,d+e+f,!
WRITE !, "The sum is: ", d,!
d proc2(9,9)
}
proc2(g,h)[a, b]{
w a+b,!
}
DHC-APP>d publicvarsexample^PHA.MOB.TEST
setting a
setting b
setting c
69
The sum is: 60
30
公有变量与私有变量
在程序中,局部变量可以是“公共的”或“私有的”。公共列表(Pubvar)声明程序中的哪些变量引用被添加到公共变量集;程序中的所有其他变量引用都是对只有当前过程调用才能看到的私有集的引用。
私有变量在进入过程时未定义,在退出过程时销毁。
当过程内的代码调用该过程外的任何代码时,私有变量将在返回该过程时恢复。被调用的程序或例程可以访问公共变量(以及它自己的私有变量)。因此,pubvar既指定了该程序看到的公共变量,也指定了该过程调用的例程能够看到的该过程中使用的变量。
如果公有变量列表为空,则所有变量都是私有的。在这种情况下,方括号是可选的。
名称以“%
”字符开头的变量通常是系统使用或用于某些特殊目的的变量。所有这样的%
变量都是隐式公共的。它们可以列在公用名单中,但不是必需的。
私有变量与使用NEW创建的变量
请注意,私有变量与使用new新创建的变量不同。 如果过程要使变量直接供其调用的其他过程或子例程使用,则该变量必须是公共变量,并且必须在公用列表中列出。如果它是此程序引入的公共变量,则对其执行new
是有意义的。这样,当我们退出过程时,它将自动销毁,并且它还保护公共变量之前可能具有的任何值。 例如,代码:
MyProc(x,y)[name]{
NEW name
SET name="John"
DO xyz^abc
QUIT
}
使例程“abc
”中的程序“xyz
”能够看到名称的值“john
”,因为它是公共的。调用name
的new
命令可以保护调用过程“MyProc
”时可能已经存在的名为“name
”的公共变量。
new
命令不影响私有变量;它只对公共变量起作用。在程序中,如果x
未在公共变量列表中列出并且x
不是%
变量,则指定new x
或new(X)
是非法的。
公开形式列表参数
如果过程具有它调用的其他过程需要的正式列表参数(如MyProc(x,y)
中的“x
”或“y
”),则该参数应列在公用列表中。
MyProc(x,y)[x] {
DO abc^rou
}
使x
的值(而不是y
的值)可用于例程“abc^rou
”。
publicvarsexample
DO proc1(4,5)
QUIT
proc1(e,f) [a, b]
{
WRITE !, "setting a" SET a = 10
WRITE !, "setting b" SET b = 20
WRITE !, "setting c" SET c = 30
SET d = a + b + c
w !,d+e+f,!
WRITE !, "The sum is: ", d,!
d proc2(9,9)
}
proc2(g,h)[a, b]{
w a+b,!
s a=40
s b=50
d proc3
}
proc3
w a+b,!
DHC-APP>d publicvarsexample^PHA.MOB.TEST
setting a
setting b
setting c
69
The sum is: 60
30
90
公共和私有程序
程序可以是“公共的”或“私有的”。私有程序只能从定义该过程的例程内调用,而公共程序可以从任何例程调用。如果省略PUBLIC
和PRIVATE
关键字,则默认为“PRIVATE
”。
定义公共程序
MyProc(x,y) PUBLIC { }
而
MyProc(x,y) PRIVATE { }
和
MyProc(x,y) { }
两者都定义了私有过程。