[Page 307]
7.14. Passing Arguments: Pass-
by-Value
vs.
Pass-by-Reference
Two ways to pass arguments to functions in many programming languages are
pass-by-value
and
pass-by-reference
. When an argument is passed by value (the default in C#), a
copy
of the argument's value is made and passed to the called function. Changes to the copy do not affect the original variable's value in the caller. This
prevents
the accidental side effects that so greatly hinder the development of correct and reliable software systems. Each argument that has been passed in the programs in this chapter so far has been passed by value. When an argument is passed by reference, the caller gives the method the ability to access and modify the caller's original variable.
|
Pass-by-reference is good for performance reasons, because it can eliminate the pass-by-value overhead of copying large amounts of data.
|
|
|
Pass-by-reference can weaken security, because the called function can corrupt the caller's data.
|
|
To pass an object by reference into a method, simply provide as an argument in the method call the variable that refers to the object. Then, in the method body, reference the object using the parameter
name
. The parameter refers to the original object in memory, so the called method can access the original object directly.
Previously, we discussed the difference between value types and reference types. One of the major differences between them is that value-type variables store values, so specifying a value-type variable in a method call
passes
a copy of that variable's value to the method. Reference-type
variables
store references to objects, so specifying a reference-type variable as an argument passes the method a copy of the actual reference that refers to the object. Even though the reference itself is passed by value, the method can still use the reference it receives to modify the original object in memory. Similarly, when returning information from a method via a
return
statement, the method returns a copy of the value stored in a value-type variable or a copy of the reference stored in a reference-type variable. When a reference is returned, the calling method can use that reference to interact with the referenced object. So, in effect, objects are always passed by reference.
What if you would like to pass a variable by reference so the called method can modify the variable's value? To do this, C# provides keywords
ref
and
out
. Applying the
ref
keyword to a parameter declaration allows you to pass a variable to a method by referencethe called method will be able to modify the original variable in the caller. The
ref
keyword is used for variables that already have been
initialized
in the calling method. Normally, when a method call contains an
uninitialized
variable as an argument, the compiler generates an error. Preceding a parameter with keyword
out
creates an
output parameter
. This indicates to the compiler that the argument will be passed into the called method by reference and that the called method will assign a value to the original variable in the caller. If the method does not assign a value to the output parameter in every possible
path
of execution, the compiler generates an error. This also prevents the compiler from generating an error message for an uninitialized variable that is passed as an argument to a method. A method can return only one value to its caller via a return statement, but can return many values by specifying multiple output parameters.
[Page 308]
You can also pass a reference-type variable by reference, which allows you to modify the passed reference-type variable so that it references a new object. Passing a reference by reference is a tricky but powerful technique that we discuss in Section 8.8.
The application in Figs. 7.18 and 7.19 uses the
ref
and
out
keywords to manipulate integer values. Class
ReferenceAndOutputParameters
(Fig. 7.18) contains three
methods
that calculate the square of an integer. Method
SquareRef
(lines 3740) multiplies its parameter
x
by itself and
assigns
the new value to
x
.
SquareRef
's parameter
x
is declared as
ref int
, which indicates that the argument passed to this method must be an integer that is passed by reference. Because the argument is passed by reference, the assignment at line 39 modifies the original argument's value in the caller.
Figure 7.18. Reference, output and value parameters.
1
// Fig. 7.18: ReferenceAndOutputParameters.cs
2
// Reference, output and value parameters.
3
using
System;
4
5
class
ReferenceAndOutputParameters
6
{
7
// call methods with reference, output and value parameters
8
public void
DemonstrateReferenceAndOutputParameters()
9
{
10
int
y = 5;
// initialize y to 5
11
int
z;
// declares z, but does not initialize it
12
13
// display original values of y and z
14
Console.WriteLine(
"Original value of y: {0}"
, y );
15
Console.WriteLine(
"Original value of z: uninitialized\n"
);
16
17
// pass y and z by reference
18
SquareRef(
ref
y );
// must use keyword ref
19
SquareOut(
out
z );
// must use keyword out
20
21
// display values of y and z after they are modified by
22
// methods SquareRef and SquareOut, respectively
23
Console.WriteLine(
"Value of y after SquareRef: {0}"
, y );
24
Console.WriteLine(
"Value of z after SquareOut: {0}\n"
, z );
25
26
// pass y and z by value
27
Square( y );
28
Square( z );
29
30
// display values of y and z after they are passed to method Square
31
// to
demonstrate
arguments passed by value are not modified
32
Console.WriteLine(
"Value of y after Square: {0}"
, y );
33
Console.WriteLine(
"Value of z after Square: {0}"
, z );
34
}
// end method DemonstrateReferenceAndOutputParameters
35
36
// uses reference parameter x to modify caller's variable
37
void
SquareRef(
ref int
x )
38
{
[Page 309]
39
x = x * x;
// squares value of caller's variable
40
}
// end method SquareRef
41
42
// uses output parameter x to assign a value
43
// to an uninitialized variable
44
void
SquareOut(
out int
x )
45
{
46
x =
6
;
// assigns a value to caller's variable
47
x = x * x;
// squares value of caller's variable
48
}
// end method SquareOut
49
50
// parameter x receives a copy of the value passed as an argument,
51
// so this method cannot modify the caller's variable
52
void
Square(
int
x )
53
{
54
x = x * x;
55
}
// end method Square
56
}
// end class ReferenceAndOutputParameters
|
Figure 7.19. Application to test class
ReferenceAndOutputParameters
.
1
// Fig. 7.19: ReferenceAndOutputParamtersTest.cs
2
// Application to test class ReferenceAndOutputParameters.
3
class
ReferenceAndOutputParamtersTest
4
{
5
static void
Main( string[] args )
6
{
7
ReferenceAndOutputParameters test =
8
new
ReferenceAndOutputParameters();
9
test.DemonstrateReferenceAndOutputParameters();
10
}
// end Main
11
}
// end class ReferenceAndOutputParamtersTest
Original value of y: 5 Original value of z: uninitialized Value of y after SquareRef: 25 Value of z after SquareOut: 36 Value of y after Square: 25 Value of z after Square: 36
|
|
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
Method
SquareOut
(lines 4448) assigns its parameter the value
6
(line 46), then squares that value.
SquareOut
's parameter is declared as
out int
, which indicates that the argument passed to this method must be an integer that is passed by reference and that the argument does not need to be initialized in advance.
Method
Square
(lines 5255) multiplies its parameter
x
by itself and assigns the new value to
x
. When this method is called, a copy of the argument is passed to the parameter
x
. Thus, even though parameter
x
is modified in the method, the original value in the caller is not modified.
[Page 310]
Method
DemonstrateReferenceAndOutputParameters
(lines 834) invokes methods
SquareRef
,
SquareOut
and
Square
. This method begins by initializing variable
y
to
5
and declaring, but not initializing, variable
z
. Lines 1819 call methods
SquareRef
and
SquareOut
. Notice that when you pass a variable to a method with a reference parameter, you must precede the argument with the same keyword (
ref
or
out
) that was used to declare the reference parameter. Lines 2324 display the values of
y
and
z
after the calls to
SquareRef
and
SquareOut
. Notice that
y
has been changed to
25
and
z
has been set to
36
.
Lines 2728 call method
Square
with
y
and
z
as arguments. In this case, both variables are passed by valueonly copies of their values are passed to
Square
. As a result, the values of
y
and
z
remain
25
and
36
, respectively. Lines 3233 output the values of
y
and
z
to show that they were not modified.
|
The
ref
and
out
arguments in a method call must match the parameters specified in the method declaration;
otherwise
, a compilation error occurs.
|
|
|
By default, C# does not allow you to choose whether to pass each argument by value or by reference. Value-types are passed by value. Objects are not passed to methods; rather, references to objects are passed to methods. The references
themselves
are passed by value. When a method receives a reference to an object, the method can manipulate the object directly, but the reference value cannot be changed to refer to a new object. In Section 8.8, you'll see that references also can be passed by reference.
|
|
|