在 ABAP 中使用控制结构

学习目标

  • 实施条件分支。

  • 处理异常。

  • 实施迭代。

条件分支

01_-__Conditional_Branching_001 01_-__Conditional_Branching_002 01_-__Conditional_Branching_003

01_-__Conditional_Branching_001 01_-__Conditional_Branching_002 01_-__Conditional_Branching_003

01_-__Conditional_Branching_001 01_-__Conditional_Branching_002 01_-__Conditional_Branching_003

01_-__Conditional_Branching_001 01_-__Conditional_Branching_002 01_-__Conditional_Branching_003

01_-__Conditional_Branching_001 01_-__Conditional_Branching_002 01_-__Conditional_Branching_003

条件分支是一种控制结构,允许您根据逻辑条件执行代码。

最常见的条件分支由一对关键字 IF 和 ENDIF 组成。仅当满足 IF 后的条件时,才会执行 IF 和 ENDIF 之间的代码块。

您可以通过使用最多一个关键字 ELSE 和任意数量的关键字 ELSEIF 扩展 IF … ENDIF 结构来添加更多代码块。通过添加关键字 ELSE,可确保始终仅执行一个代码块。如果缺少 ELSE,则可能会发生未执行任何代码块的情况。

要执行的代码块确定如下:

  • 首先,评估 IF 条件。如果满足,则执行相关代码块,且程序在 ENDIF 后继续。

  • 仅当不满足 IF 条件时,才会评估第一个 ELSEIF 后的条件。如果满足,则执行相关代码块,且程序在 ENDIF 后继续。

  • 针对所有 ELSEIF 条件连续执行此操作。如果未满足任何条件且结构包含 ELSE,则执行 ELSE 后的代码块。否则,不会执行任何代码块。

提示

与许多其他编程语言相反,ABAP 需要在每个逻辑条件之后,甚至在关键字 ELSE 之后使用分隔符 (.)。

逻辑条件是比较、逻辑运算、表达式和函数的组合,运行时系统评估这些条件以确定条件为真还是假。

逻辑条件的最常见用例是在 IF ... 中的关键字 IF 或 ELSEIF 之后。ENDIF. 结构。

第一个示例是一个简单的比较:如果两个数据对象 x 和 y 具有相同的值,则条件为真。

第二个示例更复杂:x 的值大于或等于 y 且小于 y 值的两倍,或者小于或等于 y 并大于 y 值的两倍。

第三个示例使用了算术函数 abs() 和逻辑表达式 BETWEEN <表达式 1> AND <表达式 2>。如果 x 的绝对值介于 y 的绝对值与两次 y 的绝对值之间,则条件为真。

对于简单值比较,可以使用运算符 =、<>、>、<、>= 和 <=。您不仅可以比较数据对象的值,还可以比较许多其它表达式的值,如示例中的算术表达式 2 * y。

注意

ABAP 对值分配和值比较使用相同的符号 (=)。根据职位进行区分。

您可以使用运算符 AND 和 OR 组合逻辑表达式和运算符 NOT 来否定表达式。如果没有括号,则 NOT 绑定比 AND 和 AND 强于 OR。

ABAP 知道一些特殊逻辑表达式:

  • <数据对象> 如果 <数据对象> 包含类型特定的初始值,则 IS INITIAL 为 true

  • <数据对象> 如果 <数据对象> 包含的值与类型特定的初始值不同,则 IS NOT INITIAL 为 true

  • <数据对象> BETWEEN <表达式 1> AND <表达式 2>

一些特殊的 ABAP 函数是谓词函数。这意味着它们是逻辑条件本身。Contains() 是比较字符型值的函数,line_exists() 对内部表中的行执行存在性检查。

条件分支的第二项技术是 CASE…WHEN .. ENDCASE 控制结构。

带 CASE 的条件分支 .. ENDCASE 是带有 IF 的一般分支的特殊情况… ENDIF. 如果分支取决于单个数据对象的值,并且每次都使用相等比较,可连续与一组可能的值进行比较,则可以使用 CASE。

在此示例中,将数据对象编号 的值与值 1 和 2 进行比较。如果值等于 1,则执行 <code_block_1>,如果值为 euqals 2,则改为执行 <code_blocl_2>。对于任何其他值,在 WHEN OTHERS 之后执行代码块。

具有 CASE…ENDCASE 的任何条件分支也可以通过 IF … ENDIF 结构实施。右侧示例对此进行了说明。

提示

处理特殊大小写时,应使用 CASE…ENDCASE 以提高代码的可读性。

试用:条件分支

  1. 与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

* Declarations
**********************************************************************
CONSTANTS c_number TYPE i VALUE 0.
*    CONSTANTS c_number TYPE i VALUE 1.
*    CONSTANTS c_number TYPE i VALUE 2.
*    CONSTANTS c_number TYPE i VALUE -1.
*    CONSTANTS c_number TYPE i VALUE -2.
* Example 1: Simple IF ... ENDIF.
**********************************************************************
out->write(  `--------------------------------` ).
out->write(  `Example 1: Simple IF ... ENDIF.` ).
out->write(  `-------------------------------` ).
IF c_number = 0.
out->write( `The value of C_NUMBER equals zero`   ).
ELSE.
out->write( `The value of C_NUMBER is NOT zero`   ).
ENDIF.
* Example 2: Optional Branches ELSEIF and ELSE
**********************************************************************
out->write(  `--------------------------------------------` ).
out->write(  `Example 2: Optional Branches ELSEIF and ELSE` ).
out->write(  `--------------------------------------------` ).
IF c_number = 0.
out->write( `The value of C_NUMBER equals zero`            ).
ELSEIF c_number > 0.
out->write( `The value of C_NUMBER is greater than zero`   ).
ELSE.
out->write( `The value of C_NUMBER is less than zero`      ).
ENDIF.
* Example 3: CASE ... ENDCASE
**********************************************************************
out->write(  `---------------------------` ).
out->write(  `Example 3: CASE ... ENDCASE` ).
out->write(  `---------------------------` ).
CASE c_number.
WHEN 0.
out->write( `The value of C_NUMBER equals zero`             ).
WHEN 1.
out->write( `The value of C_NUMBER equals one`              ).
WHEN 2.
out->write( `The value of C_NUMBER equals two`              ).
WHEN OTHERS.
out->write( `The value of C_NUMBER equals non of the above` ).
ENDCASE.
  1. 按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。

  2. 分析控制台输出。围绕源代码进行操作以熟悉概念;使用要查看的不同值取消注释常量 c_number 的不同声明,然后执行代码的哪些分支。

异常处理

异常

在 ABAP 中,异常是 ABAP 程序执行期间的错误情况。检测错误情况的代码引发异常。

根据引发异常的人员,我们区分系统异常和应用程序异常。

如果不进一步测量异常,则会导致运行时错误。运行时错误终止程序,缺省情况下记录在短转储中。

如果相关异常可捕获,则可以避免运行时错误。可使用语句 TRY…CATCH…ENDTRY 在程序中处理可捕获异常。

所有应用程序异常和许多系统异常均可捕获。稍后,在本课程中,您将了解如何引发应用程序异常。此时,我们将重点关注可捕获系统异常的处理。

以下视频显示了一些运行时错误的示例。

视频:本节含 SAP 官方视频(视频 ID 1_p1u9q3et),需在 learning.sap.com 在线观看。

异常处理

为防止程序因可捕获异常而终止,必须使用语句 TRY 和 ENDTRY 将异常源自的代码括起来。通过执行此操作,代码将成为 TRY…ENDTRY 结构的 TRY 块的一部分。

在 ENDTRY 语句之前,必须添加 CATCH 语句,后跟要处理的异常的 ID。或者,您可以添加多个 CATCH 语句来处理多个不同的异常。每个 CATCH 语句应后跟代码以处理此异常。此代码称为 CATCH 块。

当程序执行到达 TRY 语句时,它将继续执行 TRY 块中的代码。然后会发生三种情况:

  1. 如果在 TRY 块期间未引发异常,则忽略 CATCH 块。在 ENDTRY 语句之后继续执行。

  2. 如果在 TRY 块期间引发异常(存在匹配的 CATCH),则 TRY 块的执行将终止,并执行此异常的 CATCH 块。之后,在 ENDTRY 语句之后继续执行。

  3. 如果在不存在匹配 CATCH 的 TRY 块期间引发异常,程序会因运行时错误而终止。

现在,您已了解异常情况,让我们了解如何处理异常。

视频:本节含 SAP 官方视频(视频 ID 1_xgayu5wy),需在 learning.sap.com 在线观看。

试用:异常处理

  1. 与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

* Declarations
**********************************************************************
DATA result TYPE i.
DATA numbers TYPE TABLE OF i.
* Preparation
**********************************************************************
APPEND 123 TO numbers.
* Example 1: Conversion Error (no Number)
**********************************************************************
CONSTANTS c_text TYPE string VALUE 'ABC'.
*    CONSTANTS c_text TYPE string VALUE '123'.
out->write(  `---------------------------` ).
out->write(  `Example 1: Conversion Error` ).
out->write(  `---------------------------` ).
TRY.
result = c_text.
out->write( |Converted content is { result }|  ).
CATCH cx_sy_conversion_no_number.
out->write( |Error: { c_text } is not a number!| ).
ENDTRY.
* Example 2: Division by Zero
**********************************************************************
CONSTANTS c_number TYPE i VALUE 0.
*    CONSTANTS c_number TYPE i VALUE 7.
out->write(  `---------------------------` ).
out->write(  `Example 2: Division by Zero` ).
out->write(  `---------------------------` ).
TRY.
result = 100 / c_number.
out->write( |100 divided by { c_number } equals { result }| ).
CATCH cx_sy_zerodivide.
out->write(  `Error: Division by zero is not defined!` ).
ENDTRY.
* Example 3: Itab Error (Line Not Found)
**********************************************************************
CONSTANTS c_index TYPE i VALUE 2.
*    CONSTANTS c_index TYPE i VALUE 1.
out->write(  `-------------------------` ).
out->write(  `Example 3: Line Not Found` ).
out->write(  `-------------------------` ).
TRY.
result = numbers[ c_index ].
out->write( |Content of row { c_index } equals { result }| ).
CATCH cx_sy_itab_line_not_found.
out->write(  `Error: Itab has less than { c_index } rows!` ).
ENDTRY.
* Example 4: Combination of Different Exceptions
**********************************************************************
*    CONSTANTS c_char TYPE c LENGTH 1 VALUE 'X'.
*    CONSTANTS c_char TYPE c length 1 value '0'.
CONSTANTS c_char TYPE c LENGTH 1 VALUE '1'.
*    CONSTANTS c_char TYPE c length 1 value '2'.
out->write(  `----------------------` ).
out->write(  `Example 4: Combination` ).
out->write(  `----------------------` ).
TRY.
result = numbers[ 2 / c_char ].
out->write( |Result: { result } | ).
CATCH cx_sy_zerodivide.
out->write( `Error: Division by zero is not defined`  ).
CATCH cx_sy_conversion_no_number.
out->write( |Error: { c_char } is not a number! | ).
CATCH cx_sy_itab_line_not_found.
out->write( |Error: Itab contains less than { 2 / c_char } rows| ).
ENDTRY.
  1. 按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。

  2. 分析控制台输出。浏览源代码以熟悉概念:

  • 对异常处理进行注释,以使系统引发运行时错误。

  • 将常量 c_number 、 c_text 、 c_index 和 c_char 的值更改为不引发任何异常。

迭代

迭代是定义多次执行的代码块的控制结构。

最简单的迭代形式由一个代码块组成,这些代码块由一对语句 DO 和 ENDDO 包围。如果没有进一步措施,这将建立无限循环,必须通过以下可能性之一避免:

指定的迭代次数

通过使用整数表达式后跟关键字 TIMES 扩展 DO 语句,您可以显式指定代码块的迭代频率。整数表达式可以与数字文字一样简单,但算术计算也是一个选项。如果表达式的值等于 0,则 DO 和 ENDDO 之间的代码块根本不会执行,程序会立即在 ENDDO 之后继续代码。

基于逻辑条件中止

可以使用 EXIT 语句随时中止迭代。然后,程序继续使用 ENDDO 后的代码。请注意,迭代之外 EXIT 具有不同的作用。在其中终止当前处理块的处理,例如当前方法。

通常,EXIT 由 IF 包围,ENDIF 用于根据中止条件终止迭代。请注意,如果中止条件永远不会变为真,则此类迭代可能会变成无限循环。

当然,可以结合这两种技术,即显式指定迭代次数,然后用 EXIT 退出迭代。因此,迭代次数将成为运行时可能未达到的最大数量。

基于内部表

第三种迭代类型是 LOOP…ENDLOOP 结构,用于连续读取内部表的行。在此,迭代次数由内部表中的行数确定。

在 DO 和 ENDDO 之间的代码块中,您可以实施对 ABAP 内置数据对象 sy-index 的读取访问。此整数变量用作迭代计数器,即,ABAP 运行时会在每次新迭代开始时将其增加 1。

与其他编程语言可能使用的内容相矛盾,sy-index 在第一次迭代期间以值 1 开头。

ABAP 内置变量 sy-tabix 可以实现使用 LOOP 迭代的类似目的。但请注意,严格来说,sy-tabix 实际上不是一个计数器,但它标识在当前迭代中处理的表行的位置。稍后,在 LOOP…ENDLOOP 结构中不会处理内部表的所有行时,我们将看到差异。

试用:迭代

  1. 与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

* Declarations
**********************************************************************
CONSTANTS c_number TYPE i VALUE 3.
*    CONSTANTS c_number TYPE i VALUE 5.
*    CONSTANTS c_number TYPE i VALUE 10.
DATA number TYPE i.
* Example 1: DO ... ENDDO with TIMES
**********************************************************************
out->write(  `----------------------------------` ).
out->write(  `Example 1: DO ... ENDDO with TIMES` ).
out->write(  `----------------------------------` ).
DO c_number TIMES.
out->write(  `Hello World` ).
ENDDO.
* Example 2: DO ... ENDDO with Abort Condition
**********************************************************************
out->write(  `-------------------------------` ).
out->write(  `Example 2: With Abort Condition` ).
out->write(  `-------------------------------` ).
number = c_number * c_number.
" count backwards from number to c_number.
DO.
out->write( |{ sy-index }: Value of number: {  number }| ).
number = number - 1.
"abort condition
IF number <= c_number.
EXIT.
ENDIF.
ENDDO.
  1. 按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。

  2. 分析控制台输出。围绕源代码进行操作以熟悉概念;取消注释常量 c_number 的不同声明以查看不同值如何影响结果。