第四章 疯狂Caché 变量

第四章 变量

变量是可以存储值的位置的名称。在CachéObjectScript中,变量没有关联的数据类型,不必声明它。

通常,可以使用set命令通过为变量赋值来定义变量。可以将空字符串(“”)值赋给变量。大多数命令和函数在使用之前都需要定义变量。如果变量未定义,则默认情况下,引用它会生成<UNDEFINED>错误。通过设置%SYSTEM.Process.Unfined()方法,可以更改Caché 行为,使其在引用未定义变量时不会生成<unfined>错误。

可以在一些操作中使用未定义的变量,例如READ命令、$INCREMENT函数、$BIT函数和双参数形式的$GET函数。这些操作为变量赋值。$DATA函数可以接受未定义或已定义的变量,并返回其状态。

变量类别

局部变量

局部变量是存储在当前Caché进程中的变量。只有创建它的进程才能访问它。它被映射为可以从所有命名空间访问。当进程结束时,所有进程的局部变量都会被删除。

Caché不会将局部变量 SETKILL视为日志记录的事务事件;回滚事务对这些操作没有任何影响。

命名约定

使用以下命名约定定义局部变量:

  • 局部变量名必须是有效的标识符。其第一个字符必须是字母或百分号(%)字符。以“%”字符开头的变量名称称为“百分比变量”,具有不同的作用域规则。只有以“%Z”或“%z”开头的变量才可用于应用程序代码;所有其他百分比变量将保留给系统使用。百分号(%)字符只能用作局部变量名称的第一个字符。局部变量名的其他字符可以是字母或数字。
  • 任何单词都可以用作变量名。但是,强烈建议变量名不要是Caché ObjectScript命令或SQL保留字的名称。
  • 局部变量名称区分大小写。例如MYVAR, MyVar, 和 myvar是三个不同的局部变量。
  • 在Unicode系统上,本地变量名可以包括ASCII 255以上的字母字符(Unicode字母)。
  • 对于当前进程,局部变量名必须是唯一的。其他进程可能具有同名的局部变量。进程专用全局变量或全局变量可以与局部变量同名。例如:myvar^|| myvar^myvar是三个不同的变量。
  • 局部变量名称限制为31个字符。可以指定长度超过31个字符的名称,但仅使用前31个字符。因此,局部变量名在其前31个字符内必须是唯一的。
  • 局部变量可以采用下标。通过使用下标,可以将局部变量定义为值数组。下标可以是数字或字符串;它可以包括Unicode字符。

注意:%is实用程序使用全大写名称设置几个局部变量。在调用%is的情况下,应避免使用这些变量名。

无效命名

不遵循上述命名约定的局部变量名会生成<SYNTAX>错误。有一个例外:如果无效的变量名以下划线字符后跟字母开头,则Caché将生成一个<_callback语法>错误。(请注意错误名称中的下划线字符。)。例如,set_abc=123set x=_abc。这是因为Caché将这些名称标识为VISM控件名称,而不是本地变量名称。

类型、用法和范围

有三种局部变量:

  • 私有变量。过程块中使用的任何变量都自动成为私有变量,并且仅在该过程块中可见。默认情况下,使用CachéStudio创建的所有对象方法都使用过程块(ProcedureBlock类关键字在类定义中设置),因此,默认情况下,在方法中创建的所有变量都是私有变量。不能对私有变量使用new命令。

  • 公共变量。公共变量一旦定义,对当前进程内的任何代码都是可见的,除非满足以下条件之一:(1)已使用new命令重新定义变量,或(2)程序已进入过程块

  • %变量 名称以“%”开头的变量始终被视为公共变量。这使得定义对进程内的所有代码都可见的特殊变量成为可能。

/// d ##class(PHA.TEST.ObjectScript).TestVarOne()
ClassMethod TestVarOne()
{
	s %a="我是公共变量"
	s a="我是私有变量"
	d ..TestVarTwo()
}

/// d ##class(PHA.TEST.ObjectScript).TestVarTwo()
ClassMethod TestVarTwo()
{
	w %a,!
	w $d(a),!
	s %a="重新赋值共有变量"
	w %a,!
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestVarOne()
我是公共变量
0
重新复制共有变量
DHC-APP 3d1>zw
 
%a="重新赋值共有变量"
<Private variables>

XECUTE命令可以将它使用的变量指定为私有或公共。

可以使用不带参数的WRITEZWRITE命令列出当前定义的局部变量。可以使用$QSUBSCRIPT函数返回指定局部变量的组件(名称和下标),或使用$QLENGTH函数返回下标图层的数量。可以使用kill命令删除局部变量。

进程-私有全局变量

进程私有全局变量是一个只能由创建它的进程访问的变量。它被映射为可以从所有命名空间访问。当该进程结束时,其所有进程私有全局变量都将被删除。

进程私有全局变量旨在用于大量数据时。在许多情况下,它们可以替代mgr/temp目录的使用,在进程终止时提供自动清理功能。

Caché不会将进程私有全局的设置或终止视为日志记录的事务事件;回滚事务对这些操作没有任何影响。

命名约定

进程专用全局名称采用以下形式之一:

^||name 
^|"^"|name 
^["^"]name 
^["^",""]name

这四种前缀形式是等价的,并且所有四种形式都指相同的进程-私有全局。第一种形式(^||name)最常见,也是新代码推荐使用的形式。提供第二、第三和第四种形式是为了与定义全局变量的现有代码兼容。它们允许指定一个变量,该变量确定是将名称定义为进程私有全局还是标准全局。

下面的示例显示了这一点:

  SET x=1       // 切换存储类型
  IF x=1 {
    SET a="^"   // 进程私有全局变量
  }
  ELSE {
    SET a=""    // 标准全局
  }
  SET ^|a|name="a value"

进程专用全局变量使用以下命名约定:

  • 进程专用全局名称必须是有效的标识符。它的第一个字符(第二个竖线之后)必须是字母或百分号(%)字符。百分号(%)字符只能用作进程专用全局名称的第一个字符。只有以“%Z”或“%z”开头的百分比变量可用于应用程序代码(如^||%zmyppg^||%Z123);所有其他百分比变量保留给系统使用。

进程专用全局名称的第二个和后续字符可以是字母、数字或句点字符。句点不能用作名称的第一个或最后一个字符。

  • 进程专用全局名称不能包含Unicode字母(ASCII 255以上的字母字符)。尝试在进程专用全局名称中包含Unicode字母会导致<WIDE CHAR>错误。
  • 进程专用全局名称区分大小写。
  • 进程专用全局名称在其进程内必须是唯一的。
  • 进程专用全局名称限制为31个字符,不包括前缀字符。可以指定长度超过31个字符的名称,但仅使用前31个字符。因此,进程专用全局名称在其前31个字符内必须是唯一的。
  • 进程-私有全局变量可以接受下标。通过使用下标,可以将进程私有全局定义为一个值数组。下标可以是数字或字符串;它可以包括Unicode字符。

列表进程-私有全局变量

可以使用^GETPPGINFO实用程序以块为单位显示当前进程的名称-私有全局变量及其空间分配^GETPPGINFO不列出进程私有全局变量的下标或值。可以通过指定进程ID(PID)来显示特定进程的进程私有全局变量,也可以通过指定“*”通配符字符串来显示所有进程的进程私有全局变量。必须在%SYS命名空间中才能调用^GETPPGINFO

以下示例使用^GETPPGINFO列出所有当前进程的进程专用全局变量:


/// d ##class(PHA.TEST.ObjectScript).TestGlobal()
ClassMethod TestGlobal(Output age, ByRef ages)
{
	SET ^||TMP("PHA","PHA.IP.MOB")="yao"
	SET ^||PHA("PHA","PHA.OP.MOB")="xin"
	SET ^||flintstones(1)="Fred"
	SET ^||flintstones(2)="Wilma"
	ZNSPACE "%SYS"
	DO ^GETPPGINFO("*")
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestGlobal()
Process ID: 1408 --> Total PPG Block count 1, block size 8,192
Process ID: 1948 --> Total PPG Block count 5, block size 8,192
     PPG: %ISCQueryTemp uses 2 blocks
     PPG: %sql.temp uses 2 blocks
Process ID: 3024 --> Total PPG Block count 0, block size 8,192
Process ID: 3772 --> Total PPG Block count 0, block size 8,192
Process ID: 3856 --> Total PPG Block count 1, block size 8,192
Process ID: 7036 --> Total PPG Block count 0, block size 8,192
Process ID: 7256 --> Total PPG Block count 7, block size 8,192
     PPG: PHA uses 2 blocks
     PPG: TMP uses 2 blocks
     PPG: flintstones uses 2 blocks
Process ID: 7668 --> Total PPG Block count 0, block size 8,192
Process ID: 7840 --> Total PPG Block count 0, block size 8,192
Process ID: 7876 --> Total PPG Block count 3, block size 8,192
     PPG: %sql.temp uses 2 blocks
Process ID: 9520 --> Total PPG Block count 0, block size 8,192
Process ID: 12192 --> Total PPG Block count 0, block size 8,192
Process ID: 13608 --> Total PPG Block count 0, block size 8,192
Process ID: 13904 --> Total PPG Block count 17, block size 8,192
     PPG: %CacheStream uses 2 blocks
     PPG: %ISC.WorkQueueMgr uses 2 blocks
     PPG: %ISCQueryTemp uses 2 blocks
     PPG: %Studio.Project uses 2 blocks
     PPG: BindSrvBufferOutput uses 2 blocks
     PPG: BindSrvOutCountVar uses 2 blocks
     PPG: BindSrvOutPieceVar uses 2 blocks
     PPG: BindSrvUseCache uses 2 blocks
Process ID: 14104 --> Total PPG Block count 0, block size 8,192
 
%SYS>

^GETPPGINFO具有以下语法:

^GETPPGINFO("pdf","options","outfile")
  • pdf参数可以是进程ID或*通配符。
%SYS>d ^GETPPGINFO("7256")
Process ID: 7256 --> Total PPG Block count 7, block size 8,192
     PPG: PHA uses 2 blocks
     PPG: TMP uses 2 blocks
     PPG: flintstones uses 2 blocks
  • Options参数可以是包含以下任意组合的字符串:B(返回值(以字节为单位)、MNN(仅返回使用nn或更多块的进程私有变量);S(禁止屏幕显示;与OUTFILE一起使用);T(仅显示进程总数)。

  • outfile参数是CSV(逗号分隔值)格式的文件的文件路径,将用于接收^GETPPGINFO输出。

下面的示例将进程私有变量写入名为ppgout的输出文件。S选项禁止屏幕显示;M500选项仅将输出限制为使用500个或更多块的进程私有变量:

  ZNSPACE "%SYS"
  DO ^GETPPGINFO("*","SM500","D:/ppgout") 
    %SYS>d ^GETPPGINFO("*","MNN","D:/ppgout")
JOB,PPGNAME,BLOCKS
1948,1948.^||%ISCQueryTemp,2
1948,1948.^||%sql.temp,2
7256,7256.^||PHA,2
7256,7256.^||TMP,2
7256,7256.^||flintstones,2
7876,7876.^||%sql.temp,2
13904,13904.^||%CacheStream,2
13904,13904.^||%ISC.WorkQueueMgr,2
13904,13904.^||%ISCQueryTemp,2
13904,13904.^||%Studio.Project,2
13904,13904.^||BindSrvBufferOutput,2
13904,13904.^||BindSrvOutCountVar,2
13904,13904.^||BindSrvOutPieceVar,2
13904,13904.^||BindSrvUseCache,2

    %SYS>d ^GETPPGINFO("*","T","D:/ppgout")
JOB,BLOCKS
1408,1
1948,5
3024,0
3772,0
3856,1
7036,0
7256,7
7668,0
7840,0
7876,3
9520,0
12192,0
13608,0
13904,17
14104,0

全局变量global

全局变量是一种特殊类型的变量,它自动存储在Caché数据库中。它被映射到特定的命名空间,并且只能在该命名空间内访问,除非使用扩展引用。全局可以由任何进程访问。全局在创建它的进程终止后仍然存在。它会一直存在,直到被明确删除。

Caché将全局的设置或终止视为日志记录的事务事件;回滚事务将撤消这些操作。在提交做出更改的事务之前,锁可用于防止其他进程访问对全局的更改。

在CachéObjectScript程序中,可以像使用任何其他变量一样使用全局变量。从语法上讲,全局名称由插入符号(“^”)字符后跟字母或“%”字符来区分:

 SET mylocal = "This is a local variable"
 SET ^myglobal = "This is a global stored in the current namespace"

全局变量的命名约定如下:

  • 全局由全局前缀和全局名称组成。全局前缀通常是插入符号(^)字符,指定当前名称空间中的全局。全局前缀也可以是扩展引用,例如^|“Samples”|,指定另一个命名空间中的全局。

  • 全局名称必须是有效的标识符。它的第一个字符(前缀字符之后)必须是字母或百分号(%)字符。只有以“%Z”或“%z”开头的百分比变量可用于应用程序代码(如^%zmyglobal^%Z123);这些^%Z^%z全局变量会写入CACHESYS数据库,因此在升级在Caché时会保留这些变量。

全局名称的第二个和后续字符可以是字母、数字或句点字符。句点不能用作名称的第一个或最后一个字符。

  • 全局名称不能包含Unicode字母(ASCII 255以上的字母字符)。尝试在全局名称中包含Unicode字母会导致<Wide Char>错误。
  • 全局名称区分大小写。
  • 全局名称在其命名空间内必须是唯一的。
  • 全局名称限制为31个字符,不包括前缀字符。您可以指定长度超过31个字符的名称,但仅使用前31个字符。因此,全局名称的前31个字符必须是唯一的。
  • 全局变量可以接受下标。通过使用下标,可以将全局定义为值的数组。下标可以是数字或字符串;它可以包括Unicode字符。
  • 某些全局名称保留供系统间使用。

或者,全局可以指定一个扩展引用,该引用使用紧跟在“^”后面的一对竖线或方括号来定义其命名空间或目录(例如:^|“Samples”|myglobal^|“”|myglobal)。这些扩展的全局引用不应与进程私有全局引用混淆。

可以使用$ZREFERENCE特殊变量来确定最近使用的全局变量的名称。可以使用$QSUBSCRIPT函数返回指定全局的组件,或使用$QLENGTH函数返回下标图层的数量。

下标变量

局部变量、进程私有变量和全局变量都可以采用下标。所有类型变量的下标约定都相似:

  • 下标可以是数字或字符串。它可以包括任何字符,包括Unicode字符。有效的数字下标包括正数和负数、零和小数。空字符串(“”)不是有效的下标。

  • 下标数值区分大小写。

  • 将数字下标转换为规范形式。因此,^a(7)^a(007)^a(7.000)^a(7)。都是相同的下标。字符串下标不会转换为规范形式。因此,^a(“7”)^a(“007”)^a(“7.000”)^a(“7”)。是不同的下标。字符串下标^a(“7”)与数字下标^a(7)相同。

  • 下标的最大长度为511个编码字节(相应的字符数取决于下标中的字符和当前区域设置)。超过最大下标长度会导致错误。但是,允许的最长整数是309位;超过此限制将导致<MAXNUMBER>错误。因此,必须将长度超过309个字符的数字下标指定为字符串。

  • 局部变量的最大下标级别数为255。全局或进程专用全局的最大下标级别数为253。超过最大下标级别数会导致错误。

锁名称下标遵循与变量下标相同的约定。

数组变量

数组变量只是具有一个或多个下标级别的变量。下标用括号括起来。下标级别用逗号分隔。任何变量(特殊变量除外)都可以用作数组,如下例所示:

 SET a(1) = "A local variable array"
 SET a(1,1,1) = "Another local variable array"
 SET ^||a(1) = "A process-private global array"
 SET ^a(1) = "A global array"
 SET obj.a(1) = "A multidimensional array property"

对于局部变量,最大下标级别数为255。对于全局变量,下标级别的最大数量取决于下标名称的长度。

特殊变量

CachéObjectScript包括许多内置特殊变量(也称为系统变量),用于使某些系统信息可供应用程序使用。所有特殊变量都随Caché一起提供,并使用“$”字符前缀命名。用户不能定义其他特殊变量。将该特殊变量集映射为可从所有名称空间访问。

特殊变量的值设置为操作环境某些方面的当前状态。一些特殊变量最初设置为空字符串(“”);引用特殊变量应该不会生成<unfined>错误。特殊变量的值特定于当前进程,不能从其他进程访问。

用户可以使用SET命令设置一些特殊变量;其他特殊变量不能由用户修改。

以下示例使用特殊变量$HOROLOG

 SET starttime = $HOROLOG
 HANG 5
 WRITE !,$ZDATETIME(starttime)
 WRITE !,$ZDATETIME($HOROLOG)
03/17/2020 17:01:20
03/17/2020 17:01:25

特殊变量$HOROLOG存储当前系统日期和时间。set命令使用此特殊变量将用户定义的局部变量starttime设置为此值。然后,挂起命令将程序挂起5秒。最后,两个$ZDATETIME函数以用户可读的格式返回starttime和当前系统日期和时间。

特殊变量的其他示例包括:

 WRITE !,"$JOB = ",$JOB   // Current process ID
 WRITE !,"$ZVERSION = ",$ZVERSION   // Version info
$JOB = 16912
$ZVERSION = Cache for Windows (x86-64) 2016.2 (Build 736U) Fri Sep 30 2016 11:46:02 EDT

许多特殊变量是只读的;不能使用SET命令设置它们。其他特殊变量(如$DEVICE)是可读写的,可以使用set命令进行设置。

特殊变量不能带下标。特殊变量不能使用$INCREMENT函数递增,也不能使用KILL命令终止。特殊变量可以使用WRITEZZDUMP命令显示;不能使用ZWRITE命令显示。

对象属性

对象属性是与对象的特定实例相关联并存储在其中的值。严格地说,对象属性不是变量,但是从语法上讲,可以按照与任何其他局部变量完全相同的方式使用对象属性。

有几种类型的属性引用:oref.property..propertyi%property。对象引用(OREF)必须是局部变量,而不是表达式。属性引用可以引用单值属性或带下标的多维属性。可以有链接的属性引用,如oref.pro1.pro2

下面的示例显示用作变量的属性引用:

  // Create an Address object
  SET address = ##class(Sample.Address).%New()
  // Use the properties of the object
  SET address.City = "Boston"
  WRITE "City: ",address.City,!
City: Boston

将属性引用用作变量有一些限制。不能使用SET $BIT()SET $LISTBUILD()修改任何类型的属性;不能使用SET $EXTRACT()SET $LIST()SET $PICE()修改非多维属性。$DATA()$GET()$INCREMENT()只能在属性是多维的情况下采用该属性。属性不能与合并命令一起使用。这些操作可以使用特殊的实例变量语法i%PropertyName在对象方法中完成。

变量类型和转换

CahéObjectScript中的变量是非类型化的-没有指定的数据类型。(JavaScript、VBScript和CachéBasic也是如此。)这意味着可以将字符串值赋给变量,然后再将数值赋给同一变量。作为优化,Caché可以对字符串、整数、数字和对象使用不同的内部表示,但这对应用程序程序员是不可见的。Caché根据变量的使用上下文自动转换(或解释)变量的值。

 // set some variables
 SET a = "This is a string"
 SET b = "3 little pigs"
 SET int = 22
 SET num = 2.2
 SET obj = ##class(Sample.Person).%New()

 // Display them
 WRITE "以下是变量本身: ",!
 WRITE "a: ",a,!
 WRITE "b: ",b,!
 WRITE "int: ",int,!
 WRITE "num: ",num,!
 WRITE "obj: ",obj,!,!

 // Now use them as other "types"
 WRITE "数字类型",!
 WRITE "a, b, and obj: ",!
 WRITE "+a: ",+a,!
 WRITE "+b: ",+b,!
 WRITE "+obj: ",+obj,!,!
 
 WRITE "Here are concatenations of int and num:",!
 WRITE "Concatenating int: ","I found " _ int _ " apples.",!
 WRITE "Concatenating num: ","There are " _ num _ " pounds per kilogram.",!
以下是变量本身: 
a: This is a string
b: 3 little pigs
int: 22
num: 2.2
obj: 6@Sample.Person

数字类型
a, b, and obj: 
+a: 0
+b: 3
+obj: 6

Here are concatenations of int and num:
Concatenating int: I found 22 apples.
Concatenating num: There are 2.2 pounds per kilogram.

Caché ObjectScript类型转换规则

from to 规则
Number String 使用表示数字值的字符串,例如上例中变量num的2.2。
String Number 字符串的前导字符被解释为数字文字,例如,“-1.20abc”被解释为-1.2,而“abc123”被解释为0。
Object Number 使用给定对象引用的内部对象实例编号。该值为整数。
Object String 使用n@cls形式的字符串,其中n是内部对象实例编号,cls是给定对象的类名。
Number Object 不允许
String Object 不允许

对象值

对象值引用内存中对象的实例。可以将对象值指定给任何局部变量:

 SET person = ##class(Sample.Person).%New()
 WRITE person,!
1@Sample.Person

注意:Person的值是转换为字符串的对象引用(OREF)的值。此字符串或其值不能用于从数据库加载对象。
可以使用点语法引用对象的方法和属性:。

SET person.Name = "YaoXin"

可以使用$ISOBJECT函数确定变量是否包含对象:

 SET str = "A string"
 SET person = ##class(Sample.Person).%New()

 WRITE "Is string an object? ", $IsObject(str),!
 WRITE "Is person an object? ", $IsObject(person),!
Is string an object? 0
Is person an object? 1

不能将对象值分配给全局。这样做将导致运行时错误。

将对象值赋给变量(或对象属性)会产生增加对象的内部引用计数的副作用。当对对象的引用数量达到0时,Caché将自动销毁该对象(调用其%OnClose方法并将其从内存中删除)。例如:

 SET person = ##class(Sample.Person).%New() // 一个Person对象引用
 SET alias = person // 两个引用

 SET person = "" //一个引用 
 
 SET alias = "" //无引用对象销毁

变量声明和范围

与其他语言不同,不需要在CachéObjectScript中声明变量。

变量作用域确定变量何时对程序“可见”。在CachéObjectScript中,有两组变量作用域规则:

  • 基于过程块的标准(以及更新的)作用域机制。这是新应用程序的首选机制,也是CachéStudio使用的默认机制。在过程块中,所有非%变量都是私有变量。

  • 一种较旧的遗留作用域机制。这是为了与旧版本兼容而提供的。在过程块之外,所有变量都是公共变量。

使用#Dim

在Studio中编写代码时,可以使用#Dim预处理器指令。#Dim提供有关所需变量类型的信息。Studio使用此信息通过Studio Assist功能完成代码。此信息还可用于文档或向可能查看代码的其他人提供信息。
#Dim的语法形式为:

#Dim VariableName As DataTypeName
#Dim VariableName As List Of DataTypeName
#Dim VariableName As Array Of DataTypeName

其中,VariableName是要为其命名数据类型的变量,DataTypeName指定该数据类型。CachéStudio提供了一个菜单,可以从中选择DataTypeName值。

#Dim还允许指定变量的初始值,如下所示:

/// d ##class(PHA.TEST.ObjectScript).TestDim()
ClassMethod TestDim(Output age, ByRef ages)
{
	
	#Dim President As %String = "Obama"
	w President,!
}
Obama
原创文章 67 获赞 86 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yaoxin521123/article/details/105987807