动态访问结构成员COMPONENT … OF STRUCTURE
在ABAP中,字段符号(Field Symbol)是现有数据对象的占位符或符号名。字段符号本身不直接为数据保留物理空间,而只是指向一个分配了内存空间的数据对象。字段符号概念有点像C语言中的指针,但又不完全是指针,而是一种已解引用的指针,即用内容操作符“*”引用的指针,可以直接操作字段符号就像真正数据对象一样。如在C语言中的语句:
int a = 10;
int * p;
p = &a;
*p = 20;
其中p是C语言定义的一个指针,ABAP中的字段符号则相当于此处的 *p ,也相当于ABAP中的REF TO引用类型变量的->*解引用
字段符号可以看作仅是已经被解除引用的指针(类似于C语言中带有解引用操作符 * 的指针),但更像是C++中的引用类型(int i ;&ii= i;),即某个变量的别名,它与真真的指针还是有很大的区别的,在ABAP中引用变量则就是C语言中的指针。
FIELD-SYMBOLS <fs> { TYPE generic_type }|{ LIKE <generic_fs>|generic_para }
| {TYPE{[LINE OF] complete_type}|{REF TO type}}
| {LIKE{[LINE OF] dobj}|{REF TO dobj}}
<generic_fs>、generic_para、complete_type的说明请参考Form形式参数说明
<generic_fs>:
代指具有通用类型的FIELD-SYMBOLS
generic_para:
代指具有通用类型的形式参数(如FORM、类方法中的形参)
字段符号的分配ASSIGN
ASSIGN
{dobj[+off][(len)]}
|{ {(name)}|{dref->*}|{dobj INCREMENT inc }|{COMPONENT comp OF STRUCTURE struc} }
|{ {cref->(attr_name) }|{iref->(attr_name)}|{(class_name)=>(attr_name)}|{(class_name)=>attr }|{class=>(attr_name)} }
TO <fs>
{ }
| {CASTING { {}|{TYPE type|(name)}|{LIKE dobj} | {[TYPE p] DECIMALS dec} | {TYPE HANDLE handle} }}
| { { TYPE name } | { [TYPE name] DECIMALS dec } }
{ } | {RANGE range}.
被分配的数据dobj的长度要大小或等于<fs>的长度
只有在使用动态分配 ASSIGN (name)… 时,才会修改sy-subrc系统变量(成功时sy-subrc为0,否则为4),而静态分配 ASSIGN dobj… 时后不会修改系统变量,所以在判断静态分配方式是否成功时,只能使用 <fs> IS [NOT] ASSIGNED 语句来判断。
如果是动态分配ASSIGN (name)…不成功时,<fs>保持上一次的状态;如果是静态分配不成功时,则<fs>将会处于未分配内存的状态,这时可以使用可以使用逻辑表达式 <fs>IS [NOT] ASSIGNED 语句来判断某个字段符号是否已分配
静态分配
dobj[+off][(len)]
在指定了off的情况下,一定要指定len的长度,且区域不能超过dobj所在的数据区,否则编译不通过;
len可以是“*”来阻止分配dobj限制之外的区域给<fs>,这样就会从指定的off位置一直到dobj数据区末尾。
注:偏移量能用于固定长度的字符类型如C、D、N、T、或者是由这些类型组件组成的结构,不用于其他类型。
只能使用 <fs> IS ASSIGNED 逻辑表达式来判断静态分配是否成功
DATA text TYPE c LENGTH 4 VALUE '0123'.
FIELD-SYMBOLS <char> TYPE c.
DATA off TYPE i.
DO 4 TIMES.
off = sy-index - 1.
ASSIGN text+off(1) TO <char>.
WRITE / <char>.
ENDDO.
0
1
2
3
动态分配
只能通过sy-subrc来判断动态分配是否成功
(name)
name不一定要大写
name可以是以下这些格式:
ü dobj+offset(len)
ü struct-field
ü obj->attr
ü class=>static_attr
ü (ProgramName)DOBJ:可以访问主调程序中的数据
FIELD-SYMBOLS:<fs>.
DATA : str(20) TYPE c VALUE 'Output String',
name(20) TYPE c VALUE 'str'.
*静态分配:编译时就知道要分配的对象名
ASSIGN name TO <fs>."结果是<fs>与name变量等同
WRITE / <fs>.
*动态分配:直到运行时才知道要分配的对象名
ASSIGN (name) TO <fs>."<fs>与str变量等同
WRITE / <fs>.
<fs> = 'aaa'."由于<fs>与str是同一东西,所以只要其中一个发生变化,则另一个也会发现变化,<fs>像C++中引用类型变量,是一个别名而已
WRITE / str.
str
Output String
aaa
如果动态引用的数据对象为table work areas,则可以使用TABLE FIELD选项:
ASSIGN TABLE FIELD(<f>) TO<FS>.
其中<f>可以是普通变量的名称,也可以是某个字段符号变量名称。
主调程序:
REPORT zjdemo.
TABLES sbook.
sbook-fldate = sy-datum.
sbook-bookid = '111'.
"非TABLES定义的数据对象在子程序中是不能访问的
DATA: c TYPE c VALUE 'a'.
"(zjform1)表示调zjform1程序的form1过程
PERFORM form1(zjform1).
子程序:
REPORT zjform1.
FORM form1.
PERFORM form2(zjform2).
ENDFORM.
子子程序:
FORM form2.
"注:主调程序中也同样声明了sbook表工作区,这里并没有覆盖主调程序中的定义,但不能去掉TABLES语句
TABLES sbook.
WRITE: / 'sbook-fldate修改前:',sbook-fldate.
"这里实质上是共用主调程序中的sbook表工作区对象
sbook-fldate = sy-datum + 1.
"这里还可以定义名为sbook的变量,因为与上面不在一个命名空间
DATA: sbook TYPE c VALUE 'X'.
FIELD-SYMBOLS <fs>.
ASSIGN ('SBOOK-FLDATE') TO <fs>."这里引用到了局部变量sbook但sbook非结构,没有FLDATE字段,所以分配失败
WRITE: / sy-subrc, '动态引用全局sbook-fldate1但失败:', <fs>.
"这里引用到的是局部定义的sbook,而不是全局的
ASSIGN ('SBOOK') TO <fs>.
WRITE: / sy-subrc, '动态引用局部sbook:', <fs>.
FIELD-SYMBOLS <result>.
"可以明确的使用TABLE FIELD选项去搜索使用TABLE定义的变量,所以这个与上面不一样,引用的是全局的而非局部的SBOOK
ASSIGN TABLE FIELD ('SBOOK') TO <fs>.
WRITE: / sy-subrc, '分配全局的SBOOK成功'.
ASSIGN COMPONENT 'FLDATE' OF STRUCTURE <fs> TO <result>."组件名一定要大写
WRITE: / sy-subrc, '动态访问全局结构SBOOK-FLDATE成功:',<result>.
"这里不能使用上面这个语句,因为在该程序中已经有sbook
ASSIGN TABLE FIELD ('SBOOK-FLDATE') TO <fs>.
WRITE: / sy-subrc, '动态引用全局sbook-fldate2:',<fs>.
"还可以直接使用以下的语法访问其他程序中的变量
ASSIGN ('(ZJDEMO)SBOOK-FLDATE') TO <fs>.
WRITE: / sy-subrc, '动态引用全局sbook-fldate3:', <fs>.
"主调程序中的非TABLES定义的对象访问不到,因为不是使用TABLES定义的,所以不能在不同程序*享
ASSIGN ('C') TO <fs>.
WRITE: / sy-subrc,'尝试引用全局的C,但失败,所以<fs>显示的还是上一次成功分配的值:',<fs>.
"虽然这里访问的是当前程序中SBOOK,但该SBOOK还是共享的主调程序中的SBOOK
ASSIGN TABLE FIELD ('(ZJFORM2)SBOOK-bookid') TO <fs>.
WRITE: / sy-subrc,'引用全局的SBOOK-bookid:',<fs>.
ENDFORM.
sbook-fldate修改前: 12.09.2013
4 动态引用全局sbook-fldate1但失败:
0 动态引用局部sbook: X
0 分配全局的SBOOK成功
0 动态访问全局结构SBOOK-FLDATE成功: 13.09.2013
0 动态引用全局sbook-fldate2: 13.09.2013
0 动态引用全局sbook-fldate3: 13.09.2013
4 尝试引用全局的C,但失败,所以<fs>显示的还是上一次成功分配的值: 13.09.2013
0 引用全局的SBOOK-bookid: 00000111
*">动态访问数据对象dref->*
对数据对象解引用后,让<fs>指向它
DATA g_dat TYPE string.
DATA dref TYPE REF TO data.
FIELD-SYMBOLS <l_dat> TYPE any.
CREATE DATA dref LIKE g_dat.
ASSIGN dref->* TO <l_dat>.
WRITE <l_dat> .
动态访问结构成员COMPONENT … OF STRUCTURE
COMPONENT comp OF STRUCTURE struc
struc为结构对象。如果comp属于类型 C 或字段串,则它表示要访问的结构体中的组件名;如果comp为i整型,则表示结构体中要访问的成员的索引。
DATA: BEGIN OF line,
col1 TYPE i VALUE '11',
col2 TYPE i VALUE '22',
col3 TYPE i VALUE '33',
END OF line.
DATA comp(5) VALUE 'COL3'.
FIELD-SYMBOLS: <f1>, <f2>, <f3>.
ASSIGN line TO <f1>.
ASSIGN comp TO <f2>.
DO 3 TIMES.
"通过索引动态的访问结构成员
ASSIGN COMPONENT sy-index OF STRUCTURE <f1> TO <f3>.
WRITE <f3>.
ENDDO.
"通过成员名动态的访问结构成员
ASSIGN COMPONENT <f2> OF STRUCTURE <f1> TO <f3>.
WRITE / <f3>.
11 22 33
33
如果定义的内表没有组件名时,可以使用索引为0的组件来访问这个无名字段(注:不是1):
DATA : itab TYPE TABLE OF string WITH HEADER LINE.
itab = '11'.
FIELD-SYMBOLS <fs>.
ASSIGN COMPONENT 0 OF STRUCTURE itab TO <fs>.
WRITE: <fs>.
11
动态访问类(对象)(静态)属性
CLASS c1 DEFINITION.
PUBLIC SECTION.
DATA: attr TYPE i VALUE 11.
CLASS-DATA: static_attr TYPE i VALUE 22."静态属性
ENDCLASS.
DATA: oref TYPE REF TO c1.
CREATE OBJECT oref.
FIELD-SYMBOLS <attr> TYPE any.
ASSIGN oref->('attr') TO <attr>.
WRITE:/ <attr>."11
ASSIGN oref->('static_attr') TO <attr>.
WRITE:/ <attr>."22
ASSIGN ('c1')=>('static_attr') TO <attr>.
WRITE:/ <attr>."22
ASSIGN c1=>('static_attr') TO <attr>.
WRITE:/ <attr>."22
ASSIGN ('c1')=>static_attr TO <attr>.
WRITE:/ <attr>."22
{ }
此表示分配时不指定数据类型。
此时被分配源数据类型需要与<fs>的类型匹配
CASTING
将<fs>指向的内存区域内容以何种类型(视图)来看待
如果要将待分配的内存区域转换为C、N类型时,待分配的内存区域的字节数要是4 的倍数
待分配的内存区域必须要与指定的类型(不管是显示、还是隐式转换)对齐,即要满足以下规则:
ü 如果要转换为c、n、d、t时,待分配的内存区域的地址要能被2整除
ü 如果要转换为i时,待分配的内存区域的地址要能被4整除
ü 如果要转换为f时,待分配的内存区域的地址要能被8整除
x,c、n、d、t,i,f这四种类型之间进行转换时,需要注意对齐要求
DATA hex TYPE x LENGTH 10.
FIELD-SYMBOLS <fs> TYPE any.
"由于X类型与C类型不能对齐(X的地址不能被2整除),所以虽然编译能通过,但运行时会抛异常
"ASSIGN hex+0(4) TO <fs> CASTING TYPEc.
"能成功分配的前提有2个:
"一是要求待分配的内存的字节数为4的位数
"二是要求待分配的内存的地址要能被2整除,因为上面分配语句不满足这个条件
",所以运行进抛异常了,所以偏移一位时就肯定能被2整除了
ASSIGN hex+1(8) TO <fs> CASTING TYPE c.
{}隐式类型转换
CASTING后面不明确指定转换类型时,分配的内存区域将会以<fs>的类型来处理。
此情况下,定义的<fs>必须是完全限定类型,或者是ABAP中预定义的通用类型c, n, p, x(这4个非限定性类型即可以隐式也可以显示强转)
下面程序中尽管数据对象sy-datum中不存在year、month、day这些组件,但经过将sy-datum所在的内存区域看作是t_date类型时,就可以有这些组件了:
TYPES: BEGIN OF t_date,
year(4) TYPE n,
month(2) TYPE n,
day(2) TYPE n,
END OF t_date.
FIELD-SYMBOLS <fs> TYPE t_date."将<fs>定义成了具体限定类型
ASSIGN sy-datum TO <fs> CASTING."sy-datum变量的类型与<fs>的类型实质上是完全不兼容的,虽不兼容但可以隐式强制转换
*ASSIGN sy-datum TO <fs> CASTING TYPE t_date."完全限定类型不能进行显示的强制类型转换
WRITE: / <fs>-year, / <fs>-month, / <fs>-day.
2011
05
26
显示类型转换
显示类型情况下<fs>定义时只能是通用类型,而不能是完全限定类型。
TYPE type|(name)
type 可以是类型的字面常量,但要大小;name为类型的名称,必须是大写。
此情况下type、name不能是通用类型(但c, n, p,x这4种通用类型除外),另外也不能指定为内表类型与REFTO的类型。
DATA txt(8) TYPE c VALUE '19980606'.
FIELD-SYMBOLS <fs>.
ASSIGN txt TO <fs> CASTING TYPE d.
WRITE / <fs>.
06061998
LIKE dobj
DATA txt(8) TYPE c VALUE '19980606'.
FIELD-SYMBOLS <fs>.
ASSIGN txt TO <fs> CASTING TYPE d.
WRITE / <fs>.
06061998
[TYPE p] DECIMALS dec
会将待分配的内存区域转换为P类型,小位数由dec决定。可以省略TYPE,如果一定要指定TYPE,则类型只能指定为P
DATA factor TYPE p LENGTH 8 DECIMALS 0.
DATA pack TYPE p LENGTH 8 DECIMALS 0 VALUE '12345678'.
FIELD-SYMBOLS <pack> TYPE p.
DO 8 TIMES.
ASSIGN pack TO <pack> CASTING DECIMALS sy-index.
factor = pack / <pack>.
WRITE / factor.
ENDDO.
10
100
1.000
10.000
100.000
1.000.000
10.000.000
100.000.000
TYPE HANDLE handle
handle只能是CL_ABAP_DATADESCR或其子类的引用变量
DATA: dref TYPE REF TO data,
c20type TYPE REF TO cl_abap_elemdescr.
c20type = cl_abap_elemdescr=>get_c( 10 ).
CREATE DATA dref TYPE HANDLE c20type.
DATA: x20type TYPE REF TO cl_abap_elemdescr.
x20type = cl_abap_elemdescr=>get_x( 20 ).
FIELD-SYMBOLS: <fs> TYPE any.
ASSIGN dref->* TO <fs> CASTING TYPE HANDLE x20type.
已过时语法
TYPE name
[TYPE name] DECIMALS dec
name只能是"C", "D", "F", "I", "N", "P", "T", "X", "b", or "s"这些字面常量类型,大小写敏感
如果<fs>指定了完全限定类型,则一定要与Name相同。
DATA txt(8) VALUE '19980606'.
FIELD-SYMBOLS <fs>.
ASSIGN txt TO <fs>.
WRITE / <fs>.
"注:分配时 txt 不能比<fs>指定的类型所需空间短
ASSIGN txt TO <fs> TYPE 'D'.
WRITE / <fs>.
"类型一定要大写
ASSIGN txt TO <fs> TYPE 'X'.
WRITE / <fs>.
19980606
19980606
31003900390038003000360030003600
分配程序公共区域局部副本
程序公共区域
定义全局的工作区域接口,它能被程序组共同使用,在这之间定义的所有对象都属于公共的区域。
DATA BEGIN OF COMMON PART [name].
...
DATA END OF COMMON PART [name].
注,该定义只能写在程序的全局区域,而不能写在某个过程的里。在只定义一个这样的公共区域时,可以省略 name,否则需要一个唯一的名字。在所有需要进行访问的程序中需要进行同样的定义这个公共区域才能使用。
include文件:part
* INCLUDE part.
DATA: BEGIN OF COMMON PART struc,
f1 TYPE i,
f2 TYPE i,
s TYPE i,
END OF COMMON PART struc.
主程序文件:param
PROGRAM param.
INCLUDE part." 每个程序都需要INCLUDE一下这个公共区
PARAMETERS:
p1 TYPE i DEFAULT 20,
p2 TYPE i DEFAULT 90.
f1 = p1.
f2 = p2.
PERFORM sum IN PROGRAM sum." 调用另一程序的过程
子程序文件:sum
PROGRAM sum.
INCLUDE part.
FORM summing.
s = f1 + f2.
PERFORM display IN PROGRAM disp.
ENDFORM.
子程序文件:disp
PROGRAM disp.
INCLUDE part.
FORM display.
WRITE: / f1, f2, s.
ENDFORM.
以上三个不同的程序共用同一个公共区域,不需要使用特殊的语法来进行访问。
公共区域局部副本
ASSIGN LOCAL COPY OF ........ TO <FS>.
假设主程序 z_jzj_sapmztst 如下:
REPORT z_jzj_sapmztst.
DATA: BEGIN OF COMMON PART,
text(5) VALUE 'Text1',
END OF COMMON PART.
PERFORM routine(z_jzj_formpool2).
WRITE text.
ROUTINE过程所在的程序z_jzj_formpool2 如下:
PROGRAM z_jzj_formpool2.
DATA: BEGIN OF COMMON PART,
text(5) VALUE 'Text1',
END OF COMMON PART.
FORM routine.
FIELD-SYMBOLS <fs>.
" 现在是拷贝一份,而不是直接指向原公共区域中的对象
ASSIGN LOCAL COPY OF text TO <fs>.
WRITE <fs>.
<fs> = 'Text2'."不会修改原公共区域中的对象,而只会局部对象
WRITE <fs>.
ASSIGN text TO <fs>.
WRITE <fs>.
<fs> = 'Text3'."修改公共区域中的对象
ENDFORM.
Text1 Text2 Text1 Text3
解除分配UNASSIGN
UNASSIGN <FS>.
该语句是初始化<FS>字段符号,语句执行后,字段符号将不再引用内存区域(它指向的内存区域不会受影响),逻辑表达式<fs> IS ASSIGNED将会返回假。
CLEAR<fs>
与UNASSIGN不同的是,只有一个作用就是初始化它所指向的内存区域,而字段符号本身并没有被解除分配
DATA: c VALUE 'a'.
FIELD-SYMBOLS: <fs1>,<fs2>.
ASSIGN c TO <fs1>.
ASSIGN c TO <fs2>.
WRITE: / <fs1>,<fs2>.
UNASSIGN <fs1>.
IF NOT <fs1> IS ASSIGNED.
WRITE: / 'fs1 is unassigned'.
ENDIF.
WRITE: / '<fs2>=',<fs2>.
CLEAR: <fs2>.
IF <fs2> IS ASSIGNED.
WRITE: / 'fs2 is assigned'.
ENDIF.
WRITE: / '<fs2>=',<fs2>.
a a
fs1 is unassigned
<fs2>= a
fs2 is assigned
<fs2>=