Changing, exporting – why I don’t like them
Do you use changing and exporting parameters in your ABAP methods? I did it a long time. But I’m cured. It all began, when I startet to consider functional programming style (see this excellent blog ). It lead me to avoid changing variable values after having set them once.
Changing: A simple example
class lcl_app implementation.
method main.
data ls_line type gty_s_line_to_change.
ls_line-field1 = p_field1.
ls_line-field2 = `Value 2`.
ls_line-field3 = `Value 3`.
try.
change_field2( changing cs_line = ls_line ).
cl_demo_output=>display( ls_line ).
catch lcx_excpt.
cl_demo_output=>display( ls_line ).
endtry.
endmethod.
method change_field2.
cs_line-field2 = `Value 2 changed`.
if cs_line-field1 = `Oh no!`.
raise exception type lcx_excpt.
endif.
endmethod.
endclass.
In the above code, a changing parameter is used in method change_field2. This method changes one of the fields in the structure passed by changing.
What happens when an exception occurs? Since the change of FIELD2 is done before the RAISE, the field has changed even if the method was interrupted by the exception. If I run this program and put “Oh no!” in the parameter on the selection screen, I get this output:
A better way to achieve this could be:
methods:
change_field2
importing is_line type gty_s_line_to_change
returning value(rv_res) type string
raising lcx_excpt.
(...)
class lcl_app implementation.
method main.
data ls_line type gty_s_line_to_change.
ls_line-field1 = p_field1.
ls_line-field2 = `Value 2`.
ls_line-field3 = `Value 3`.
try.
data(ls_line_changed) = value #(
base ls_line
field2 = change_field2( ls_line ) ).
cl_demo_output=>display( ls_line_changed ).
catch lcx_excpt.
cl_demo_output=>display( ls_line_changed ).
endtry.
endmethod.
method change_field2.
rv_res = `Value 2 changed`.
if is_line-field1 = `Oh no!`.
raise exception type lcx_excpt.
endif.
endmethod.
endclass.
In this case, you get:
Another issue with changing parameters is that they do not allow using “data(var)” declarations.
Exporting parameters
Well, I really like the functional form of methods which allows to make chainings:
zcl_queue_processor=>process( zcl_queue_provider=>get( ) ).
So, if it comes to several exporting parameters, I tend to create a structure containing all the values I want to get from my method so I can create the method using a return parameter.
Another annoying thing is that exporting parameters that are passed by reference behave exactly as changing parameters. They keep the value of the passed variable if they are not changed in the called method. This is why the extended syntax check throws a warning when you do not initialize them at the beginning of the method.
Performance issues
Since ABAP knows how to “lazy copy”, i.e. to keep a reference on a copied value until the copied one changes, the disadvantage on performance using the value-based RETURNING par are not too huge. However, when it comes to large tables that are to be changed afterwards, I fall back to a reference EXPORTING var.
Conclusion
All this made me set up a personal programming style rule:
“Whenever possible, use only importing and returning parameters in methods”
What do you think about it?
One could argue does it make sense to use the parameters if an exception is raised? In the end, the method was not able to fulfill its purpose (you raised an exception). So does it make sense to process the parameters? In my opinion no.
I try too to avoid changing parameter as much a possible, however in the case of exporting i always try to clear them before anything happens.
However, the value has changed after the exception has been raised. Maybe it is used further on in a different context. With changing / exporting, you have to care about these things, with returning not.
As Horst Keller once pointed out,
you can pass a CHANGING parameter by value.
JNN
Yes, you are right. As long as I do not forget ticking the "value" checkbox in a changing parameter, I'm save. On the other hand, with returning, I can't forget it, it's mandatory.
But "Pass-by-Value" has a performance aspect as well. (cf. https://blogs.sap.com/2017/07/30/the-mysteries-of-the-returning-parameter)
I would give a closer look at the performance when my return parameter is an internal table, which might contain bulk data.
If you read the blog you mentioned, you will find there that the performance loss you have using returning parameters is about 1/2 percent. The blogger measured the following:
As you see, the duration of passing a huge data table with returning is almost the same as with exporting. In fact, it was this blog that made me give up the exporting parameters and fancy the returning.
I have read the blog but i would take it with a pinch of salt. I would expect the performance to depend on the width of the table-line. The table in example USR02(44 fields in my system) is narrower than say BSEG(393).
If you see my blog comment, i am all in with using internal tables as RETURNING but i would pay a special eye on the performance.
What ABAP methods? LOL 🙂
It should be just USING and CHANGING, in/out, whatever you call it. The rest is blasphemy. Too many choices, too many problems. Keep it simple, that's what I think.
Another option:
We stated in our development guides that larger pieces of data must be returned using a real reference (REF TO).
On the other hand this approach has a downside: When does ABAP learn that the result of a method can be dereferenced directly?
To me, this approach goes exactly in the opposite direction. Working with data references means, that if you change your data in method A it will change also in method B without being able to understand where the change came from if not searching in all methods where the reference has been used.
I am not sure if I expressed myself correctly. I am not speaking of passing references into a method and change them. I am just talking about the RETURNING parameters. Every caller can be sure to get a fresh data instance when receiving data from a method by a RETURNING parameter.
Can you give a code example for that? I'm afraid I am completely confused.