数据引用与字段符号

数据引用与字段符号 (★★★)

#data-reference #internal-tables

概述

特性 数据引用 字段符号
声明 TYPE REF TO DATA FIELD-SYMBOLS <fs> TYPE ...
赋值 GET REFERENCE OF ... INTO dref ASSIGN ... TO <fs>
访问 dref->*(解引用) <fs>(直接使用)
检查有效性 IS BOUND IS ASSIGNED
取消 CLEAR dref UNASSIGN <fs>
内存模型 指向数据对象的指针 数据对象的别名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
数据引用 vs 字段符号:
+-------------------------------------------------+
| |
| 数据对象 dref (TYPE REF TO DATA) |
| +-----------+ +-----------+ |
| | 值:42 | <----| 地址 | |
| +-----------+ +-----------+ |
| ^ | |
| | | dref->* |
| +-------------------+ (解引用) |
| |
| 数据对象 <fs> (FIELD-SYMBOL) |
| +-----------+ +-----------+ |
| | 值:42 | <----| 别名 | |
| +-----------+ +-----------+ |
| ^ | |
| | | <fs> |
| +-------------------+ (直接访问) |
| |
+-------------------------------------------------+

数据引用

声明和创建

1
2
3
4
5
6
7
8
9
10
" 声明
DATA: lr_data TYPE REF TO DATA.

" 获取已有数据对象的引用
DATA: lv_value TYPE i VALUE 42.
GET REFERENCE OF lv_value INTO lr_data.

" 在运行时创建匿名数据对象
CREATE DATA lr_data TYPE i.
CREATE DATA lr_data TYPE (lv_type_name). " 动态类型

解引用

1
2
3
" 通过解引用访问值
lr_data->* = 100.
WRITE: / lr_data->*.
⚠️解引用未绑定的引用(IS NOT BOUND)会导致运行时错误(CX_SY_REF_IS_INITIAL)。解引用前始终检查 IS BOUND

IS BOUND vs IS INITIAL

检查 含义
lr_data IS BOUND 引用指向一个数据对象
lr_data IS INITIAL 引用是初始的(空指针)
lr_data IS NOT BOUND 引用不指向任何对象
1
2
3
4
5
IF lr_data IS BOUND.
WRITE: / lr_data->*.
ELSE.
WRITE: / '引用未绑定'.
ENDIF.
📌IS BOUNDIS INITIAL 对于引用变量是对立的。检查有效性时使用 IS BOUND 更清晰。

字段符号

声明和赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
" 声明
FIELD-SYMBOLS: <fs> TYPE i,
<fs_any> TYPE any, " 泛型
<fs_struc> TYPE struc. " 结构化

" 赋值数据对象
ASSIGN lv_value TO <fs>.

" 带类型转换的赋值
ASSIGN lv_hex TO <fs> TYPE c. " 将字节解释为字符

" 取消赋值
UNASSIGN <fs>.

" 检查赋值
IF <fs> IS ASSIGNED.
WRITE: / <fs>.
ENDIF.
检查 含义
<fs> IS ASSIGNED 字段符号指向一个数据对象
<fs> IS NOT ASSIGNED 字段符号未赋值
💡UNASSIGN <fs> 后,字段符号不再赋值。访问未赋值的字段符号会导致运行时错误。

与内表配合使用

访问方法概述

语句 使用字段符号 使用数据引用
LOOP AT ASSIGNING <fs> REFERENCE INTO dref
READ TABLE ASSIGNING <fs> REFERENCE INTO dref
INSERT ASSIGNING <fs> REFERENCE INTO dref
MODIFY 隐式(无额外语法) 不适用

LOOP AT … ASSIGNING

1
2
3
4
" 通过字段符号直接修改 - 无数据复制
LOOP AT lt_itab ASSIGNING FIELD-SYMBOL(<fs_row>).
<fs_row>-amount = <fs_row>-amount * 2. " 直接修改表
ENDLOOP.

LOOP AT … REFERENCE INTO

1
2
3
4
" 每行的引用
LOOP AT lt_itab REFERENCE INTO DATA(lr_row).
lr_row->*-amount = lr_row->*-amount * 2. " 通过解引用修改表
ENDLOOP.

READ TABLE … ASSIGNING

1
2
3
4
5
6
7
" 读取到字段符号
READ TABLE lt_itab ASSIGNING FIELD-SYMBOL(<fs_row>)
WITH TABLE KEY id = '123'.

IF sy-subrc = 0.
<fs_row>-status = 'X'. " 直接修改
ENDIF.

READ TABLE … REFERENCE INTO

1
2
3
4
5
6
7
" 读取到引用
READ TABLE lt_itab REFERENCE INTO DATA(lr_row)
WITH TABLE KEY id = '123'.

IF sy-subrc = 0.
lr_row->*-status = 'X'. " 通过解引用修改
ENDIF.

INSERT … ASSIGNING / REFERENCE INTO

1
2
3
4
5
6
" 插入并获取新行的引用/字段符号
INSERT ls_wa INTO TABLE lt_itab ASSIGNING FIELD-SYMBOL(<fs_new>).
INSERT ls_wa INTO TABLE lt_itab REFERENCE INTO DATA(lr_new).

" 继续操作新行
<fs_new>-created_at = sy-uzeit.

使用字段符号的 MODIFY

1
2
3
4
5
" 字段符号直接修改表行 - 不需要 MODIFY 语句!
LOOP AT lt_itab ASSIGNING FIELD-SYMBOL(<fs_row>).
<fs_row>-processed = abap_true.
" 不需要 MODIFY;<fs_row> 已经指向表行
ENDLOOP.

性能优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+----------------------------------------------------------+
| 数据复制比较 |
+----------------------------------------------------------+
| |
| INTO wa: 将行数据复制到工作区 |
| +---------+ +---------+ 必须使用 MODIFY |
| | 表 | --> | WA 副本 | 写回到表 |
| +---------+ +---------+ (双重复制) |
| |
| ASSIGNING <fs>:无复制 - 直接指针 |
| +---------+ +---------+ 通过 <fs> 的更改 |
| | 表 | <-- | <fs> | 直接写入表 |
| +---------+ +---------+ (零复制) |
| |
| REFERENCE INTO:无复制 - 引用指针 |
| +---------+ +---------+ 通过 dref->* 的更改 |
| | 表 | <-- | dref | 直接写入表 |
| +---------+ +---------+ (零复制) |
| |
+----------------------------------------------------------+
方法 数据复制 修改方式 性能
INTO wa 是(复制进 + 复制回) 需要 MODIFY 最慢
ASSIGNING <fs> 无复制 通过 <fs> 直接 最快
REFERENCE INTO dref 无复制 通过 dref->*
💡循环内表时始终使用 ASSIGNING <fs>REFERENCE INTO dref,特别是大表。修改行时避免使用 INTO wa

完整方法比较

1
2
3
4
5
6
7
8
9
操作              INTO wa        ASSIGNING <fs>    REFERENCE INTO dref
---------------- ----------- -------------- -------------------
LOOP AT itab 复制行 无复制 无复制
READ TABLE 复制行 无复制 无复制
INSERT 不适用 获取新行 <fs> 获取新行 dref
MODIFY 需要 不需要 不适用

数据访问 wa-field <fs>-field dref->*-field
检查有效性 sy-subrc IS ASSIGNED IS BOUND

考试/测试模式

关键词/模式 答案
“ASSIGNING vs INTO” ASSIGNING = 无复制,直接修改;INTO = 数据复制
“REFERENCE INTO” 到行的数据引用;通过 dref->* 访问
“IS BOUND” 引用变量指向一个数据对象
“IS ASSIGNED” 字段符号指向一个数据对象
“用 ->* 解引用” dref->* 访问被引用的数据对象
“泛型引用解引用” TYPE REF TO DATA 必须使用 ASSIGN dref->* TO <fs> 处理泛型
“使用字段符号的 MODIFY” 不需要;对 <fs> 的更改直接修改表
“性能” ASSIGNING 和 REFERENCE INTO 避免数据复制
“GET REFERENCE OF” 获取已有数据对象的引用指针
“CREATE DATA” 在运行时创建匿名数据对象
“UNASSIGN” 移除字段符号的赋值
“insert + assigning” INSERT ... INTO TABLE ... ASSIGNING <fs> 获取新行的访问

相关笔记