使用合适的内表类别
选择合适的表类别。对于小型表,索引或散列键可能是多余的,但对于大表,始终使用以下规则:
- 索引访问:标准表
- 索引访问和键访问:排序表
- 只访问关键字:哈希表
特别是对于行数较多的表格,必须谨慎选择合适的表格类别。以下建议主要源自处理速度要求:
- 标准表
如果主要访问类型是顺序处理或索引访问,则可以始终使用该表类别。填写标准表时,应使用 APPEND
添加行,其他访问则使用索引规范(相应语句的 INDEX
语句)。理想情况下,补充数据的过程应与其他访问数据进行分离。在进行显式排序后,还可以使用二进制搜索(BINARY SEARCH)来优化键访问(自由键),这样就可以为顺序处理(LOOP)等确定一个入口点。不过,由于采用线性搜索,如果内部表的主要访问类型是键访问,那么大型标准表(超过 100 行)就不适合作为表类。
- 排序表
如果既需要快速键访问,又需要索引访问,还需要在填表时对行进行排序,那么这类表就很有用。此外,排序表还适用于 LOOP 循环中的部分顺序访问,其中在 WHERE 条件中指定了表键的第一部分。最后,在无法定义唯一键的情况下,具有模糊键的排序表是哈希表的唯一选择。
- 哈希表
如果键访问是数据表的核心操作,那么这种表类就很有用。
选择适当的方式来访问表行
可以通过访问各个行(使用 READ TABLE
或表表达式)或按顺序(使用 LOOP AT
)来读取内表。在这两种情况下,读取时可以通过三种方式存储内表的访问行:
INTO
将行复制到结构中ASSIGNING
将行分配给字段符号,这使得可以直接寻址该行REFERENCE INTO
创建对行的引用
表格表达式也是如此,只是根据结果的类别来选择行存储类型。
原则是:
- 如果行类型较窄且不需修改读取的行,则使用工作区(结构)
- 如果行类型为宽或深并且要修改读取的行,则使用字段符号
- 如果行类型为宽或深并且要传递对读取行的引用,请使用引用
出于性能原因,最好避免循环中的行复制
细节
选择输出行为的标准一方面是处理速度,另一方面是要对读取行执行的操作:
- 如果要修改读取行的内容,通常应使用添加
ASSIGNING
或(在表表达式的情况下)适当的结果。这允许使用值语义直接访问行,并且无需稍后进行MODIFY
操作。 - 如果需要对读取行的引用可以使用引用语义进行处理,则将使用添加
REFERENCE INTO
或(在表表达式的情况下)适当的结果。 - 如果不修改读取行的内容,则可以使用这些过程中的任何一个。表的行类型对于性能非常重要。如果表行很宽或包含较深的组件(例如,字符串或其他表),则如果使用
ASSIGNING
或REFERENCE INTO
而不是 INTO,读取通常会更快。它们的使用方式是选择使用两者中哪一个的决定因素。
当处理行平坦且占用空间不超过大约 1KB 的表时,使用 INTO
进行复制(至少对于 READ
语句而言)比配置动态访问所需的管理更快。对于语句 LOOP
,这些成本仅产生一次,因此始终建议在一定数量的行以上使用 ASSIGNING
或 REFERENCE INTO
。相反,如果要修改目标区域而不影响内部表,则应始终使用 INTO
。
不好的方式
以下源代码显示了将内表的行分配给工作区的目的,目的是修改读取的行。然而,对于此修改,需要附加语句 MODIFY
,并且每次循环都会发生两个不必要的复制过程。
ABAP
LOOP AT itab INTO wa.
...
wa = ...
MODIFY itab FROM wa.
ENDLOOP.
好的方式
以下源代码更正了上面的示例;这里,使用字段符号来直接访问以修改读取的行。不会产生不必要的复制成本。
ABAP
LOOP AT itab ASSIGNING <fs>.
...
<fs> = ...
ENDLOOP.
不要在循环中修改整个表
循环访问内表时,不要执行会修改整个表体的语句。仅逐行修改表。
除了用于编辑内表中各个行的语句之外,还可以使用其他语句来寻址和修改表的整个主体。例子:
- 适用于整个内表的所有分配类别
- 使用 CLEAR 或 FREE 删除整个内表
- 目标范围内的操作,例如 SELECT INTO TABLE
您不能使用跨内部表的循环来执行对表的访问,从而一次性修改整个表主体。
对整个表体的修改访问通常会产生运行时错误,并且至少会产生不可预测的程序行为。如果可以静态检测到这一点,则当使用相关表操作时,类内以及具有可静态检测的辅助键的循环中也会发生语法错误。否则,出于兼容性原因,语法检查只会返回警告。
使用 RETURN
退出例程
仅使用 RETURN
退出方法、函数、表单等。不要使用 CHECK
或 EXIT
。
不要在对话框模块和事件块中实现逻辑
相反,调用封装逻辑实现的相关类方法。
除了过程之外,还有两种类型的处理块。但是,它们没有参数接口,并且不允许声明本地数据:(AT SELECTION-SCREEN
和 GET
是例外,但不应被利用):
- 对话模块:对话模块使用语句
MODULE
引入,并使用语句ENDMODULE
结束。这些模块形成了经典 dynpro 和相关 ABAP 程序之间的功能接口。它们是从 dynpro 流逻辑内部调用的。 - 事件块:事件块由相应的关键字引入,并由下一个处理块隐式结束。当相关事件发生时,ABAP 运行时环境会触发事件块的处理。
事件块用于:
- 加载程序(LOAD-OF-PROGRAM)
- 报告可执行程序(带逻辑数据库)处理过程中发生的事件(初始化、开始选择、获取、结束选择)
- 选择屏幕事件(AT SELECTION-SCREEN ...)
- 经典列表处理的列表事件(AT LINE-SELECTION、AT USER-COMMAND)
原则: 不要在对话框模块和事件块中实现逻辑
由于无法在对话框模块和事件块中声明本地数据,因此您无法实现任何有用的程序逻辑。因此,对话框模块和事件块------只要它们仍然是必要的------应该只包含一个方法调用。如果您一致使用 ABAP 对象,则仅需要以下元素:
- 在使用类池以外的程序类型的情况下,将
LOAD-OF-PROGRAM
或INITIALIZATION
作为程序构造函数 - 处理经典 dynpros 和选择屏幕时的对话框模块和
AT SELECTION-SCREEN
- 用于后台处理的可执行程序中的
START-OF-SELECTION
为了提高可读性,您应该始终显式指定该语句(尽管在许多情况下它是可选的) - 尽管在语法上是可能的,但您不应在程序中多次指定事件块
Note:在函数组中使用
LOAD-OF-PROGRAM
基本上与在全局类中使用静态构造函数相同。在可执行程序中,如果需要评估使用SUBMIT
传递的任何参数,则可以使用INITIALIZATION
代替。
例子
以下源代码显示了包 SABAP_DEMOS_CAR_RENTAL_DYNPRO 中功能组 DEMO_CR_CAR_RENTAL_SCREENS 的 PAI 模块。可以使用事务 DEMO_CR_CAR_RENTAL 调用此包中的屏幕。这些对话框模块遵循上述规则。它们不包含自己的实现。他们调用函数组的本地类的方法。
ABAP
MODULE cancel INPUT.
screen_handler=>cancel( ).
ENDMODULE.
MODULE user_command_0100 INPUT.
screen_handler=>user_command_0100( ).
ENDMODULE.
MODULE customers_mark INPUT.
customer_table=>mark( ).
ENDMODULE.
MODULE reservations_mark INPUT.
reservation_table=>mark( ).
ENDMODULE.
仅在特殊情况下使用宏
尽可能避免使用宏。宏有几个缺点:
- 无法调试;
- 没有语法检查;
- 隐式调用接口;
- 没有接口参数类型检查。
为隐式调用的消息创建锚点
当您隐式传递消息属性(类、数字、参数)时,例如通过函数或方法调用,或者使用变量,使用锚点让消息可以在使用位置列表中搜索。
可以通过以下方式完成(因为它是在标准中完成的):
ABAP
IF 1 = 2.
MESSAGE i123(abc) ... .
ENDIF.
或者:
scss
MESSAGE i123(abc) ... INTO sy-msgli. "and then use sy-msg* fields to pass the message attributes