Flylib.com

Books Software

 
 
 

Section 7.14. Passing Arguments: Pass-by-Value vs. Pass-by-Reference


[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.

Performance Tip 7.1

Pass-by-reference is good for performance reasons, because it can eliminate the pass-by-value overhead of copying large amounts of data.


Software Engineering Observation 7.5

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.

Common Programming Error 7.12

The ref and out arguments in a method call must match the parameters specified in the method declaration; otherwise , a compilation error occurs.


Software Engineering Observation 7.6

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.