模塊化技術


ABAP program結構

image156

以下兩種Processing blocks 是由 ABAP runtime system在運行時自動調用的:

? Event blocks

? Dialog modules

以下兩種Processing blocks 由在 ABAP programs中手動編程調用的:

? Subroutines

? Function modules

? Methods

上面這三種Processing blocks是從ABAP programs編程調用的所以也叫procedures

 

SubroutinesForm)一般供內部(局部)使用,而Function modules是借外部(全局)使用

Macros

宏與INCLUDE相似但只能在同一程序內部使用。

DEFINE<macro>.

<statements>

END-OF-DEFINITION.

可以使用占位符(&1, &2, ..., &9)來使用調用時傳遞進的參數。

必須在使用宏之前進行定義。

宏不能調用自己,但可以調用其他的宏。

 

<macro> [<p1><p2> ... <p9>].

 

DATA: result TYPE i,
n1
TYPE i VALUE 5,
n2
TYPE i VALUE 6.
DEFINE operation.
  result
= &1 &2 &3.
 
output &1 &2 &3 result.
END-OF-DEFINITION.
DEFINE output.
 
write: / 'The result of &1 &2 &3 is', &4.
END-OF-DEFINITION.
operation
4 + 3.
operation
2 ** 7.
operation n2
- n1.

 

Include

Include programs 是一種全局的R/3 Repository objects

Includeprograms可以包括其他的Include文件Includeprograms不能包括自己

 

ABAP程序中的局部與全局變量

Dialog ModulesAT SELECTION-SCREEN…事件塊、GET事件塊、以及methods(類中的方法)、 subroutinesFORM子過程)、function modulesFunction函數)中聲明的變量為局部的,即在這些塊里聲明的變量不能在其他塊里使用,但這些局部變量可以覆蓋同名的全局變量除這些處理塊外,其他塊里聲明的變量都屬於全局的,效果與在程序最開頭定義的變量效果是一樣的,所以可以在其他處理塊直接使用(但要注意的是,需遵守先定義后使用的原則,這種先后關系是從語句書寫順序來說的,與事件塊的本身運行順序沒有關系);另外,局部變量聲明時,不管在處理塊的任何地方,其效果都是相當於處理塊的全局變量,而不像其他語言如Java那樣:局部變量的作用域可以存在於任何花括號{}之間(這就意味着局部變量在處理過程范圍內是全局的),如下面的i,在ABAP語言中還是會累加輸出,而不會永遠是1(在Java語言中會是1):

FORM aa.
 
DO 10 TIMES.
   
DATA: i TYPE i VALUE 0.
   
i = i + 1.
   
WRITE: / i.
 
ENDDO.
ENDFORM.

 

下面的示例中,在END-OF-SELECTION中聲明的變量,在START-OF-SELECTION中可以直接使用:

END-OF-SELECTION.
 
DATA: c VALUE 'A'.
START-OF-SELECTION.
 
WRITE:/ c.

A

但如果是這樣,則編譯不能通過(所以只與書寫順序有關,有塊的運行順序無關):

START-OF-SELECTION.
 
WRITE:/ c.

END-OF-SELECTION.
 
DATA: c VALUE 'A'.

 

Form里,有時我們不想改變全局定義的變量,一般采用的方式是在Form里聲明與全局定義相同的變量(這樣會隱藏同名的全局變量)。實質上也可以使用LOCAL語句來使全局變量局部化(即拷貝一份),修改局部化的變量不會影響全局的變量:

TABLES sflight.
PERFORM tabtest1.

WRITE: / sflight-planetype, sflight-price.
PERFORM tabtest2.
WRITE: / sflight-planetype, sflight-price.

FORM tabtest1.
  sflight
-planetype = 'A310'.
  sflight
-price = '150.00'.
 
WRITE: / sflight-planetype, sflight-price.
ENDFORM.                                                    "TABTEST1

FORM tabtest2.

 
LOCAL sflight.
  sflight
-planetype = 'B747'.
  sflight
-price = '500.00'.
 
WRITE: / sflight-planetype, sflight-price.
ENDFORM.                                                    "TABTEST2

image157

注:LOCAL語句只能用在FORMENDFORM語句之間。LOCAL語句阻止了局部變量對全局變量的隱藏

 

Form中聲明的類型TYPESDATA變量,會隱藏全局同名類型與變量的定義,所以我們在在From定義變量時,一般以“L_”為前綴:

TYPES word(10) TYPE c.
DATA text TYPE word.
text = '1234567890'.
WRITE / text.
PERFORM datatest.
WRITE / text.

FORM datatest.
 
TYPES word(5) TYPE c.
 
DATA text TYPE word.
 
"使用的是局部定義的變量,隱藏了全局同名變量
 
text = 'ABCDEFGHJK'.
 
WRITE / text.
ENDFORM

image158

 

如果想在Form運行完后,Form中的數據還需要保留到下次繼續使用,則可以在Form中使用定義STATICS來定義變量(注:雖然是STATICS屬於一個全局定義,但只在定義的Form中可見):

PERFORM datatest1.
PERFORM datatest1.
SKIP.
PERFORM datatest2.
PERFORM datatest2.

FORM datatest1.
 
TYPES f_word(5) TYPE c.
 
DATA f_text TYPE f_word VALUE 'INIT'.
 
WRITE f_text.
  f_text
= '12345'.
 
WRITE f_text.
ENDFORM.                                            

FORM datatest2.
 
TYPES f_word(5) TYPE c.

 
STATICS f_text TYPE f_word VALUE 'INIT'.
 
WRITE f_text.
  f_text
= 'ABCDE'.
 
WRITE f_text.
ENDFORM.  

image159

Local Copies of Global FieldsForm中,全局變量的局部化拷貝

Form中,可以為全局變量創建局部副本:

ASSIGN LOCAL COPY OF <f> TO <FS>.

subroutineForm)子過程中,系統會在local stack中對全局數據創建一local拷貝並使用local field symbol<FS>來指向它這樣我們就可能通過字段符號<FS>來使用拷貝的數據而不會影響全局變量數據。

注:<FS>字符符號一定要是在Form中定義的局部變量,否則編譯不通過。

 

針對所有變量,可以使用帶LOCAL COPY OF選項的ASSIGN語句,但ASSIGN COMPONENT不能帶此選項。

 

 

其他可用於Form中的ASSIGN LOCAL COPY OF語句:

ASSIGN LOCAL COPY OF INITIAL<f> TO <FS>.

該語句也會創建一份local拷貝,但不會將<f>的內容拷貝過來,只拷貝變量的類型結構,內存清0

 

ASSIGN LOCAL COPY OF INITIAL LINE OF <itab> TO <FS>

ASSIGN LOCAL COPY OF INITIAL LINE OF (<itab>) TO <FS>.

以上兩句對都是針對全局內表<itab>的行進行局部化拷貝,且拷貝過來的局部內存內容清0

 

另一實例請參考這里

 

DATA text(5) VALUE 'Text1'.
PERFORM routine.
WRITE text.

FORM routine.
 
FIELD-SYMBOLS <fs>.
 
"此語句中的<fs>不會修改全局變量 text
 
ASSIGN LOCAL COPY OF text TO <fs>.
 
WRITE <fs>.
  <fs>
= 'Text2'.
 
WRITE <fs>.
 
"此語句中的<fs>會修改全局變量 text
 
ASSIGN text TO <fs>.
 
WRITE <fs>.
 
<fs> = 'Text3'.
ENDFORM.      

image160

FORM subroutine

FORM subr [TABLES t1 [{TYPE itab_type}|{LIKE itab}|{STRUCTURE struc}]
t2 […]] 

[USING { VALUE(p1)|p1 } [ { TYPE generic_type }

| { LIKE <generic_fs>|generic_para }
| {
TYPE {[LINE OF] complete_type}|{REF TO
type} }
| {
LIKE {[LINE OF] dobj} | {REF TO
dobj} }
|
STRUCTURE
struc]

{ VALUE(p2)|p2 } […]] 

[CHANGING{ VALUE(p1)|p1 } [ { TYPE generic_type }

| { LIKE <generic_fs>|generic_para } 

| { TYPE {[LINE OF] complete_type} | {REF TO type} }
| {
LIKE {[LINE OF] dobj} | {REF TO
dobj} }
|
STRUCTURE
struc]

{ VALUE(p2)|p2 } […]] 

[RAISING {exc1|RESUMABLE(exc1)} {exc2|RESUMABLE(exc2)} ...].

 

generic_type:為通用類型,通用類型請參考通用類型

complete_type:為完全限制類型

<generic_fs>:為字段符號變量類型,如下面的 fs 形式參數

generic_para:為另一個形式參數類型,如下面的 b 形式參數

DATA: d(10) VALUE'11'.
FIELD-SYMBOLS: <fs> LIKE d.
ASSIGN d TO <fs>.
PERFORM aa USING <fs> d d.
FORM aa
USING fs like <fs>
            
a like
d
             
b like a.

 
WRITE:fs,/ a , / b.
ENDFORM.

 

如果沒有給形式參數指定類,則為ANY類型

 

如果TABLESUSINGCHANGING一起使用時,則一定要按照TABLESUSINGCHANGING順序聲明

 

值傳遞中的VALUE關鍵字只是在FORM定義時出現,在調用時PERFORM語句中無需出現,也就是說,調用時值傳遞和引用傳遞不存在語法格式差別。

TABLES table_parameters

如果使用的是TABLESTYPELIKE后面只能接標准內表類型或標准內表對象如果以STRUCTURE則使用行結構類型或結構對象。

注意:TABLES中的TYPE LIKE 后面只能使用標准內表,如果要使用排序內表或者哈希內表,則只能使用USINGCHANGING方式來代替

 

TABLES定義的形式參數是自帶一個表頭與使用默認Key的內表。當把一個帶表頭的實參傳遞給Form時,表頭也會傳遞過去,如果實際參數不帶表頭或者只傳遞了表體(使用了[]時),系統會自動為內表參數變量創建一個Form局部空的表頭

DATA: tab TYPE STANDARD TABLE OF i WITH HEADER LINE WITH NON-UNIQUE DEFAULT KEY.
DATA: line LIKE LINE OF tab.
tab
= 2."只是給表頭賦值
PERFORM fr_form TABLES tab."
注,這里的tab后面不能帶[],否則表頭會傳遞不過去
LOOP AT  tab INTO line.
 
WRITE:/ line.
ENDLOOP.
FORM fr_form TABLES itab LIKE tab[]."這里傳遞類似+
中的引用參數,即別名傳遞,即形參與實參是同一內存變量

 "實參數的表頭也會傳遞過來
 
WRITE: / itab.
 
"使用TABLES 定義的形式參數默認就會帶有表頭
  itab
= 1.
 
APPEND itab.
ENDFORM.

         2

         1

 

不管是以TABLES還是以USINGCHANGE非值方式傳遞時,都是以類似於C++中的引用參數傳遞,即別名傳遞,即形參與實參是同一內存變量,不會再在被調用的Form中再將實參拷貝一份

TABLES是一種過時的內表參數定義法可以使用USING CHANGING代替

DATA: tab TYPE HASHED TABLE OF i WITH HEADER LINE WITH UNIQUE DEFAULT KEY.
DATA: line LIKE LINE OF tab,
      tab2
like tab[].

tab = 1.
INSERT tab INTO TABLE
tab.
PERFORM fr_form USING tab[].
LOOP AT  tab INTO line.
 
WRITE:/ line.
ENDLOOP.

"使用USING定義的形式內表itab不會自帶表頭
FORM fr_form USING itab LIKE tab[]."也是別名傳遞
  line
 = 2.
 
INSERT line INTO TABLE itab
.
  "如果不注掉下面語句,則上面不會輸出結果

itab = tab2. "重新給字段符號變量內存賦值,實質上操作的還是主調函數內表內存
ENDFORM.

         1

         2

 

"如果使用值傳遞則不會修改主調程序中的實參
FORM fr_form USING VALUE(itab) LIKE tab[]."拷貝傳遞
  itab
= tab2. "不會修改主調程序中的實參
ENDFORM.

USING /CHANGING parameters

ABAP中,如果FORM中是以非值方式(即沒有VALUE選項)定義形式參數,USINGCHANGING則都是字段符號的方式(即別名)進行傳遞,即形參實際上是主調函數傳遞過來的實參的一個別名(至於傳遞的內容是地址還是直接為變量內容,則要看定義形式參數時所指定的參數類型是地址類型——ref to,還是非ref to),在被調的Form中操作字段符號就是直接對主調函數實參的內存進行操作,所以ABAP的子過程中以非值方式定義的形參就是C++的引用類型參數傳遞;另外 TABLES 傳遞參數的方式也是以字段符號傳遞如果FORM的參數定義成Value傳值方式,則在傳遞的過程時會對實參進行拷貝后再傳遞,至於傳遞的是地址還是直接為變量內容,則同樣要看形參是否類型為 REF TO 類型

 

image161

注意:上表中的引用傳遞即ABAP中的字段符號(別名)傳遞,而不是說傳遞的實參內容為真正的傳址,至於傳遞的是地址還是直接為變量內容,則同樣要看形參類型是否定義為了 REF TO 類型

 

通過引用傳遞時,USINGCHANGING完全一樣(此時CHANGING不會在等Form執行完后再去修改實參內容,而是只要修改語句執行,即修改了實參);但CHANGING為值傳遞方式時,需要在Form執行完后,才去真正修改實參變量的內容,所以CHANGING傳值與傳引用其結果都是一樣:修改了實參內容,只是修改的時機不太一樣

 

CHANGING值傳遞又稱為值和結果傳遞

FORM … CHANGING ... VALUE(P1) ... VALUE(P2)

該傳遞中“值(VALUE)”指的是在子程序調用“期間”,是值傳遞方式,即在FORM中是不能直接對主調程序中的實參進行修改,但在子程序正常結束時(ENDFORM, RETURN, CHECK, or EXIT),將把對形式參數的更改“結果”“復制”給實參,因而主程序中的實參數據對象值仍然會被修改(這種方式實際上是一種雙向數據拷貝,數據傳遞和返回都需要拷貝)。但是一旦子程序非正常中斷(If the subroutine is ended by a errormessage or an exception),參數變量仍然保持原值。

DATA: op1 TYPE i,
          op2
TYPE i,
          res
TYPE i.
op1
= 3.
op2
= 4.
PERFORM multip USING op1 op2 CHANGING res.
WRITE: / 'After subroutine:',/ 'RES=' UNDER 'RES=', res.

FORM multip USING value(o1) value(o2)

                  
 CHANGING VALUE(r).
  r
= o1 * o2.
 
WRITE: / 'Inside subroutine:', / 'R=', r, 'RES=', res.
ENDFORM.

image162

CHANGING VALUE要到Form執行結束后才改變外面的值

FORM multip USING value(o1) value(o2)
                   
CHANGING r.
 
r = o1 * o2.
 
WRITE: / 'Inside subroutine:', / 'R=', r, 'RES=', res.
ENDFORM.

image163

RAISING exc

RAISING {exc1|RESUMABLE(exc1)} {exc2|RESUMABLE(exc2)} ...

這里的exc1exc2…可以是CX_STATIC_CHECK CX_DYNAMIC_CHECK或者是其子類如果Form中出現了運行時錯誤Form簽名又沒有使用RAISING向上拋則程序會直接掛掉所以最好是向上拋

 

FORM subform RAISING cx_static_check cx_dynamic_check.
...

ENDFORM.

 

STRUCTURE struc

DATA: BEGIN OF line,
    col1
,
    col2
,
END OF line.
DATA text(2) VALUE 'XY'.
PERFORM demo USING text.
FORM demo USING
p STRUCTURE line.
 
WRITE: p-col1, p-col2.
ENDFORM.       

image164

傳遞的實參將會以line結構變量數據類型視圖來操作。注:實參的長度不能小數形參定義的長度。

FORM引用傳遞研究

DATA : i TYPE i VALUE 100.
WRITE: / 'frm_ref===='
.
PERFORM frm_ref USING i
.
WRITE: / i."200


WRITE: / 'frm_val===='.
i = 100
.
PERFORM frm_val USING i
.
WRITE: / i."100


WRITE: / 'frm_ref2===='.

"不能將下面的變量定義到frm_ref2過程中,如果這樣,下面的dref指針在調用frm_ref2 后,指向的是Form中局部變量內存,為不安全發布,運行會拋異常,因為From結束后,它所擁有的所有變量內存空間會釋放掉
DATA: i_frm_ref2 TYPE i VALUE 400.
i = 100
.
DATA: dref TYPE REF TO i
.
get REFERENCE OF i INTO
dref.
PERFORM frm_ref2 USING dref ."
傳遞的內容為地址,但還是屬於引用傳遞
WRITE: / i."4000

field-SYMBOLS : <fs> TYPE i .
ASSIGN dref->* to <fs>."
由於frm_ref2過程中已修改了dref的指向,現指向了i_frm_ref2 變量的內存空間
WRITE: / <fs>."400

WRITE: / 'frm_val2===='.
i = 100
.
DATA: dref2 TYPE REF TO i
.
get REFERENCE OF i INTO
dref2.
PERFORM frm_val2 USING
dref2 .
WRITE: / i."4000

ASSIGN dref2->* to <fs>.
WRITE: / <fs>."4000


FORM
 frm_ref  USING  p_i TYPE i ."C++中的引用參數傳遞p_i為實參i的別名
 
WRITE: /  p_i."100
  p_i =
200."p_i為參數i的別名,所以可以直接修改實參
ENDFORM.  

FORM
 frm_val  USING   value(p_i)."傳值p_i為實參i的拷貝
 
WRITE: /  p_i."100
  p_i =
300."由於是傳值,所以不會修改主調程序中的實參的值
ENDFORM.

FORM
 frm_ref2 USINGp_i TYPE REF TO i ."p_i為實參dref的別名,類似C++中的引用參數傳遞(傳遞的內容為地址,並且屬於引用傳遞——別名)
 
field-SYMBOLS : <fs> TYPE i .
 
"
現在<fs>就是實參所指向的內存內容的別名,代表實參所指向的實際內容
 
ASSIGN p_i->* to <fs>.
 
WRITE: /  <fs>."100

<fs> =
4000."
直接修改實參所指向的實際內存


 
DATA: dref TYPE REF TO i .
 
get REFERENCE OF i_frm_ref2 INTO
dref.
 
"
由於USINGC++的引用參數,所以這里修改的直接是實參所存儲的
 
"地址內容,這里的p_i為傳進來的dref的別名,是同一個變量,所以
 
"實參的指向也發生了改變(這與Java中傳遞引用是不一樣的,Java
 
"傳遞引用時為地址的拷貝,即Java中永遠也只有傳值,但C/C++/ABAP中可以傳遞真正引用——別名)
  p_i = dref.
"此處會修改實參的指向 
ENDFORM
.

FORM
 frm_val2 USING VALUE(p_i) TYPE REF TO i ."p_i為實參dref2的拷貝,類似Java中的引用傳遞(雖然傳遞的內容為地址,但傳遞的方式屬於地址拷貝——值傳遞)
 
field-SYMBOLS : <fs> TYPE i .
 
"
現在<fs>就是實參所指向的內存內容的別名,代表實參所指向的實際內容
 
ASSIGN p_i->* to <fs>.
 
WRITE: /  <fs>."100

<fs> =
4000."
但這里還是可以直接修改實參所指向的實際內容


 
DATA: dref TYPE REF TO i .
 
get REFERENCE OF i_frm_ref2 INTO
dref.
 
"
這里與過程 frm_ref2 不一樣,該過程 frm_val2 參數的傳遞方式與
 
"java中的引用傳遞是原理是一樣的:傳遞的是地址拷貝,所以下面不會修改主調程序中實參dref2的指向,它所改變的只是拷貝過來的Form中局部形式參數的指向
  p_i = dref. 
ENDFORM.

調用PERFORM

PERFORM <subr>(<prog>) [USING ... <pi>... ][CHANGING... <pi>... ] [IF FOUND].

prog只能是靜態指定,其外面的括號表示的是一種外部Form調用,而不是動態變量的意思,值為程序名字符串字面常量

IF FOUND選項表示如果不存在指定的subr,則不會執行,也不會報錯

當從外面調用時,如果prog程序未加載,則會將它加載到當前的internal session

 

如果動態指定Form名與程序名,則這樣使用:

PERFORM (<fsubr>)[IN PROGRAM (<fprog>)][USING ... <pi>... ]

[CHANGING... <pi>... ]

[IF FOUND].

這里的括號表示動態指定名稱,如果去掉,則與上面語法相當

 

 

PERFORM <idx> OF <subr1><subr2>.... <subrn>.

該語句只能調用內部的Form,並且不能帶有參數。該語句會根據指定的位置idx來調用后面指定的Form,比如,idx = 2 時,則會調用subr2

 

 

DATA: a1 TYPE p DECIMALS 3,
          a2
TYPE i,
          a3
TYPE d,
          a4
TYPE spfli-carrid,
          a5
TYPE c
.
"下面三種調用效果是一樣的
PERFORM subr USING a1 a2 a3 a4 a5.
PERFORM subr CHANGING a1 a2 a3 a4 a5.
PERFORM subr USING a1 a2 a3 CHANGING a4 a5.

FORM subr USING
         
value(f1) TYPE p
         
value(f2) TYPE i

          f3
LIKE a3
         
CHANGING

         
value(f4) TYPE spfli-carrid
          f5
.

ENDFORM

 

Function Modules

Function Groups

功能組是function modules的容器。不能直接運行function group

當你調用一個function module時,系統加將整個function group加載到主調程序所在的internal session中(如果還未加載時)。

當使用Function Builder創建函數組時,系統會自動創建main program與相應的include 程序

image165

 

上圖中,

l  <fgrp>Function Group的名稱,SAPL<fgrp>為主程序名,它將Function Group里的所有Include文件包括進來(所以如果在Function Module里使用到了某個Include文件里定義的變量或Form時,就不需要再在Function ModuleInclude這里文件了)。它除了INCLUDE語句之外,沒有其他語句了。

l  L<fgrp>TOP,里面有FUNCTION-POOL語句(FUNCTION-POOL語句類似於REPORT或者PROGRAM語句),以及整個函數都可以使用的全局數據定義。

l  L<fgrp>UXX,也只有INCLUDE句,它所包括的Include文件為相應具體Function Module所對應Include文件名:L<fgrp>U01, L<fgrp>U02, ...這些Include文件實際上包含了所對應的Function Module代碼(即雙擊它們進去就是對應的Function,而顯示的不是真正Include文件代碼)。

l  L<fgrp>U01,和L<fgrp>U02中的0102編號對應L<fgrp>UXX中的“XX”,代表其創建先后的序號,例如L<fgrp>U01L<fgrp>U02是頭兩個被創建的函數,在函數組中創建出的函數代碼就放在相應的L<fgrp>UXX(這里的XX代表某個數字,而不是字面上的XXInclude頭文件中了。

l  L<fgrg>FXX,用來存一些Form子過程,並且可以被所有Function Modules所使用(不是針對某個Function Module的,但一般在設計時會針對每個Function Module設計這樣單獨的Include文件,這是一個好習慣),並且在使用時不需要在Function Module中使用INCLUDE語句包含它們(因為這些文件在主程序SAPL<fgrp>里就已經被Include進來了——從這里也可以看出這些Include文件中定義的Form是所有Function所能共享的)。另外, L<fgrg>FXX中的F是指Form的意思,這是一種名稱約束而已,在創建時我們可以隨便指定,一般還有IXX(表示些類Include文件包括的是一些PAI事件中調用的Module,有時干脆直接使用L<fgrg>PAI或者L<fgrg>PAIXX),OXX(表示些類Include文件包括的是一些PBO事件中調用的Module,有時干脆直接使用L<fgrg>PBO或者L<fgrg>PBOXX)。

如果Form只被單獨函數所使用,可以將這些Form直接定義在Function Module里的ENDFUNCTION語句后面

 

當一個Function Module被調用的時候,該Function Module所在的整個Function Group都被裝入內存,然后該Function Module得到執行,該Function Group一直保留在內存中,直到調用Function Module的程序關閉。這樣當該Function Group中的另外一個Function Module被調用的時候,就不用重新裝載整個Function Group到內存,而可以直接訪問Function Group中的數據對象、屏幕、或子程序等,並且當被調入內存的Function Group中的某一個全局變量被其中一個Function Module更改時,則同一個Function Group中的其他Function Module可能同進受到該全局變量的狀態的影響。

 

image166

image167

image168

image169

函數實參的修改

l  import 用於定義輸入參數。此情況下如果使用傳值,在定義參數時鈎選Pass Value 選項后,函數頭注釋會發現參數類型為VALUE;如果傳引用,即未鈎選Pass Value 選項,函數頭注釋會發現參數類型為REFERENCE。在函數里,引用類型且為Import類型的實參是不能修改的,如果試着修改外面傳遞進來的實參,則編譯出錯(值傳遞類型的輸入參數可以修改,但修改后,主程序中的實參根本沒有被修改)。Import類型的參數除了可選的外,在調用時必須輸入

l  export 用於定義輸出參數。在函數中可以直接修改實參的值。也分傳值與傳址。

l  changing 用於定義輸入輸出參數。通過此類型的參數,可以修改實參的值。另外,也分傳值與傳址。

l  Tables 用於定義內表參數,與過程中的TABLES定義的參數完全一樣(在傳遞過程中只是傳字段符號,即傳址),請參考過程的內表參數

l  Exception 用於定義可能出現的異常。

 

過程與函數中的參數如果以非值方式傳遞,則表示以ABAP中的字段符號進行傳遞,即C++的引用類型參數(其實ABAP中所有參數傳遞只有傳值與傳字段符號(別名,即所謂的傳址)兩種方式,至於傳遞的內容到底地址還是非地址,則要看過程與函數的參數在定義時是否是 TYPE REF TO LIKE REF TO 方式)。

 

CALL FUNCTION <module>

[EXPORTING f1 = a1.... fn = an]

[IMPORTING f1 = a1.... fn = an]

[CHANGING f1 = a1.... fn = an]

[TABLES f1 = a1.... fn = an]

[EXCEPTIONSe1= r1.... en= rn [ERROR_MESSAGE= rE][OTHERS = ro]].

 

上面fn都是形參,an都是實參。

EXCEPTIONS:可以處理功能模塊程序代碼中發生的異常。如果在調用時出現ei異常,系統停止執行功能模塊,並且不將任何參數值從功能模塊傳遞給主調程序。如果在EXCEPTION選項中指定了ei異常,主調程序會將ri分配給SY-SUBRCri必須是數字常量。如果在異常列表中指定了ERROR_MESSAGE,你將會影響Function Module中的message處理(MESSAGE ... RAISING語句只能在function modules中使用),在Call Function異常列表中使用系統隱式異常ERROR_MESSAGE來迫使系統忽略掉Function Module中的MESSAGE ... RAISING語句中的RAISING選項(Call Function異常列表中在只有ERROR_MESSAGE異常的情況下),將MESSAGE ... RAISING語句當作普通的MESSAGE語句來處理,並且消息會在主調程序中被ERROR_MESSAGE異常所捕獲,具體捕獲過程與MESSAGE的類型相關:

l  類型為S, I, and W的消息將會直接被忽略,也不會顯示消息

l  如果是E and A類型的消息,將會被ERROR_MESSAGE異常所捕獲,並且將SY-SUBRC設置成<rE>

 

關於ERROR_MESSAGE的詳細使用,請參考《User Dialogs.docx》文檔中的《CALL FUNCTION[error_message]》章節。

 

OTHERS類型的異常能捕獲到所有除EXCEPTIONS選項后面的異常列表

 

 

因為功能模塊可以被任何程序所調用,在功能模塊中的參數只能參照內置基本類型、通用類型以及數據字典類型進行定義的類型,而不能是程序中自定義類型。

 

image170

image171

image172

function fuc_ref .
*"-------------------------------------------------------------------

*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(I_I1) TYPE  I REFERENCE(別名)為參數的默認傳遞類型
*"     VALUE(I_I2) TYPE  I定義時勾選了Pass Value選項才會是 VALUE類型
*"     REFERENCE(I_I3) TYPE REF TO  I
*"     VALUE(I_I4) TYPE REF TO  I
*"  EXPORTING
*"     REFERENCE(E_I1) TYPE  I
*"     VALUE(E_I2) TYPE  I
*"     REFERENCE(E_I3) TYPE REF TO  I
*"     VALUE(E_I4) TYPE REF TO  I
*"  TABLES
*"      T_1 TYPE  ZJZJ_ITAB
*"  CHANGING
*"     REFERENCE(C_I1) TYPE  I
*"     VALUE(C_I2) TYPE  I
*"     REFERENCE(C_I3) TYPE REF TO  I
*"     VALUE(C_I4) TYPE REF TO  I
*"-------------------------------------------------------------------

write: / i_i1."1
"由於i_i1為輸入類型參數,實參不能被修改。這里i_i1
"C++中的引用(別名)參數方式傳遞參數,所以如果修改了i_i1
"會修改實際參數,所以函數中不能修改REFERENCE IMPORTING類型的參數,如果去掉下面注釋則編譯出錯
"i_i1 = 10.

write: / i_i2."2
"雖然i_i2IMPORTING類型的參數,但不是REFERENCE類型,所以可以修改,編譯能通過
"但不會修改外面實參的值,只會影響該函數局部
i_i2 =
20.

field-symbols: <fs> type i
.
assign i_i3->* to
<fs>.
"
由於i_i3存儲的是地址,所以先要解引用再能使用
write: / <fs>.
"
同上面,REFERENCEIMPORTING類型的參數不能被修改:這里即不能修改實參的指向
"GET REFERENCE OF 30 INTO i_i3.
"雖然不可以修改實參的指向,但可以修改實參所指向的實際內容
<fs> =
30.

assign i_i4->* to
<fs>.
"i_i4
存儲也的是地址,所以先要解引用再能使用
write: / <fs>.
"
只會修改函數中的局部參數i_i4的指向,但並不會修改實參的指向,所以可以修改
get reference of 40 into
i_i4.
"
雖然不能修改實參的指向,但可以直接修改實參的所指向的實際內容
<fs> =
400.

WRITE: / c_i1."111

"c_i1為實參的別名,修改形參就等於修改實參內容
c_i1 =
1110.

WRITE: / c_i2."222

"c_i2為實參的副本,所以不會影響實參的內容,但是,
"由於是CHANGING類型的參數,且為值傳遞,在函數正常執行完后,還是
"會將該副本再次拷貝給實參,所以最終實參還是會被修改
c_i2 =
2220
.
ENDFUNCTION
.

 

調用程序:

DATA: i_i1 TYPE i VALUE 1,
      i_i2
TYPE i VALUE 2
,
      i_i3
TYPE REF TO i
,
      i_i4
TYPE REF TO i
,
      c_i1
TYPE i VALUE 111
,
      c_i2
TYPE i VALUE 222
,
      c_i3
TYPE REF TO i
,
      c_i4
TYPE REF TO i
,
      t_1
TYPE zjzj_itab WITH HEADER LINE
.

DATA: i_i3_ TYPE i VALUE 3
.
GET REFERENCE OF i_i3_ INTO
i_i3.
DATA: i_i4_ TYPE i VALUE 4
.
GET REFERENCE OF i_i4_ INTO
i_i4.
DATA: c_i3_ TYPE i VALUE 333
.
GET REFERENCE OF c_i3_ INTO
c_i3.
DATA: c_i4_ TYPE i VALUE 444
.
GET REFERENCE OF c_i4_ INTO
c_i4.

CALL FUNCTION 'FUC_REF'

 
EXPORTING
    i_i1 = i_i1
    i_i2 = i_i2
    i_i3 = i_i3
    i_i4 = i_i4
 
TABLES
    t_1 = t_1
 
CHANGING
    c_i1 = c_i1
    c_i2 = c_i2
    c_i3 = c_i3
    c_i4 = c_i4.

WRITE: / i_i2."2
WRITE: / i_i3_."30
WRITE: / i_i4_."400
WRITE: / c_i1."1110
WRITE: / c_i2."2220

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM