2. When the app executes, another compiler (known as the just-in-time compiler
8.8 Passing Arrays by Value and by Reference
Figure 8.13 next demonstrates that when a copy of an individual value-type array ele- ment is passed to a method, modifying the copy in the called method doesnotaffect the original value of that element in the calling method’s array. To show the value ofarray[3]
before invoking methodModifyElement, lines 27–29 output the value ofarray[3], which is8. Line 31 calls methodModifyElementand passesarray[3]as an argument. Remember thatarray[3]is actually oneintvalue (8) inarray. Therefore, the app passes a copy of the value ofarray[3]. MethodModifyElement(lines 44–49) multiplies the value received as an argument by 2, stores the result in its parameterelement, then outputs the value of
element (16). Since method parameters, like local variables, cease to exist when the method in which they’re declared completes execution, the method parameterelementis destroyed when methodModifyElementterminates. Thus, when the app returns control toMain, lines 32–33 output the unmodified value ofarray[3](i.e.,8).
8.8 Passing Arrays by Value and by Reference
In C#, a variable that “stores” an object, such as an array, does not actually store the object itself. Instead, such a variable stores areferenceto the object. The distinction between ref- erence-type variables and value-type variables raises some subtle issues that you must un- derstand to create secure, stable programs.
As you know, when an app passes an argument to a method, the called method receives acopyof that argument’s value. Changes to thelocal copyin the called method do notaffect the original variable in the caller. If the argument is of a reference type, the method makes acopyof thereference,notacopyof the actual object that’s referenced. The local copy of the reference also refers to the original object, which means that changes to the object in the called method affect the original object.
In Section 7.16, you learned that C# allows variables to be passed by reference with keywordref. You can also use keywordrefto pass areference-type variableby reference, which allows the called method to modify the original variable in the caller and make that variable refer to a different object. This is a subtle capability, which, if misused, can lead to problems. For instance, when a reference-type object like an array is passed withref, the called method actually gains control over thereference itself, allowing the called method to replace the original reference in the caller with a reference to a different object, or even withnull. Such behavior can lead tounpredictable effects, which can be disastrous in mis- sion-critical apps. The app in Fig. 8.14 demonstrates the subtle difference between passing a reference by value and passing a reference by reference with keywordref.
Effects of passing array element value:
array[3] before ModifyElement: 8 Value of element in ModifyElement: 16 array[3] after ModifyElement: 8
Performance Tip 8.1
Passing references to arrays and other objects makes sense for performance reasons. If arrays were passed by value, a copy of each element would be passed. For large, frequently passed arrays, this would waste time and consume considerable storage for the copies of the arrays.
Fig. 8.13 | Passing arrays and individual array elements to methods. (Part 3 of 3.)
1 // Fig. 8.14: ArrayReferenceTest.cs
2 // Testing the effects of passing array references 3 // by value and by reference.
4 using System;
5
6 public class ArrayReferenceTest 7 {
8 public static void Main( string[] args )
9 {
10 // create and initialize firstArray 11 int[] firstArray = { 1, 2, 3 };
12
13 // copy the reference in variable firstArray 14 int[] firstArrayCopy = firstArray;
15
16 Console.WriteLine(
17 "Test passing firstArray reference by value" );
18
19 Console.Write( "\nContents of firstArray " + 20 "before calling FirstDouble:\n\t" );
21
22 // display contents of firstArray
23 for ( int i = 0; i < firstArray.Length; ++i ) 24 Console.Write( "{0} ", firstArray[ i ] );
25 26 27 28
29 Console.Write( "\n\nContents of firstArray after " + 30 "calling FirstDouble\n\t" );
31
32 // display contents of firstArray
33 for ( int i = 0; i < firstArray.Length; ++i ) 34 Console.Write( "{0} ", firstArray[ i ] );
35
36 // test whether reference was changed by FirstDouble 37 if ( firstArray == firstArrayCopy )
38 Console.WriteLine(
39 "\n\nThe references refer to the same array" );
40 else
41 Console.WriteLine(
42 "\n\nThe references refer to different arrays" );
43
44 // create and initialize secondArray 45 int[] secondArray = { 1, 2, 3 };
46
47 // copy the reference in variable secondArray 48 int[] secondArrayCopy = secondArray;
49
50 Console.WriteLine( "\nTest passing secondArray " + 51 "reference by reference" );
Fig. 8.14 | Passing an array reference by value and by reference. (Part 1 of 3.)
// pass variable firstArray by value to FirstDouble FirstDouble( firstArray );
8.8 Passing Arrays by Value and by Reference 309
52
53 Console.Write( "\nContents of secondArray " + 54 "before calling SecondDouble:\n\t" );
55
56 // display contents of secondArray before method call 57 for ( int i = 0; i < secondArray.Length; ++i ) 58 Console.Write( "{0} ", secondArray[ i ] );
59 60 61 62
63 Console.Write( "\n\nContents of secondArray " + 64 "after calling SecondDouble:\n\t" );
65
66 // display contents of secondArray after method call 67 for ( int i = 0; i < secondArray.Length; ++i ) 68 Console.Write( "{0} ", secondArray[ i ] );
69
70 // test whether reference was changed by SecondDouble 71 if ( secondArray == secondArrayCopy )
72 Console.WriteLine(
73 "\n\nThe references refer to the same array" );
74 else
75 Console.WriteLine(
76 "\n\nThe references refer to different arrays" );
77 } // end Main 78
79 // modify elements of array and attempt to modify reference 80 public static void FirstDouble( )
81 {
82 // double each element's value
83 for ( int i = 0; i < array.Length; ++i )
84 array[ i ] *= 2;
85 86 87
88 } // end method FirstDouble 89
90 // modify elements of array and change reference array 91 // to refer to a new array
92 public static void SecondDouble( )
93 {
94 // double each element's value
95 for ( int i = 0; i < array.Length; ++i )
96 array[ i ] *= 2;
97 98 99
100 } // end method SecondDouble 101 } // end class ArrayReferenceTest
Fig. 8.14 | Passing an array reference by value and by reference. (Part 2 of 3.)
// pass variable secondArray by reference to SecondDouble SecondDouble( ref secondArray );
int[] array
// create new object and assign its reference to array array = new int[] { 11, 12, 13 };
ref int[] array
// create new object and assign its reference to array array = new int[] { 11, 12, 13 };
Lines 11 and 14 declare two integer array variables,firstArrayandfirstArrayCopy. Line 11 initializesfirstArraywith the values1,2and3. The assignment statement at line 14 copies the reference stored infirstArrayto variablefirstArrayCopy, causing these variables to reference the same array object. We make the copy of the reference so that we can determine later whether referencefirstArraygets overwritten. Theforstatement at lines 23–24 displays the contents offirstArraybefore it’s passed to methodFirstDouble
(line 27) so that we can verify that the called method indeed changes the array’s contents.
MethodFirstDouble
Theforstatement in methodFirstDouble(lines 83–84) multiplies the values of all the elements in the array by2. Line 87 creates a new array containing the values11,12and13, and assigns the array’s reference to parameterarrayin an attempt to overwrite reference
firstArray in the caller—this, of course, does not happen, because the reference was passed by value. After methodFirstDoubleexecutes, theforstatement at lines 33–34 dis- plays the contents offirstArray, demonstrating that the values of the elements have been changed by the method. Theif…elsestatement at lines 37–42 uses the==operator to compare referencesfirstArray(which we just attempted to overwrite) andfirstArray- Copy. The expression in line 37 evaluates totrueif the operands of operator==reference the same object. In this case, the object represented byfirstArrayis the array created in line 11—not the array created in methodFirstDouble(line 87)—so the original reference stored infirstArraywas not modified.
MethodSecondDouble
Lines 45–76 perform similar tests, using array variables secondArray and second-
ArrayCopy, and methodSecondDouble(lines 92–100). MethodSecondDoubleperforms the same operations asFirstDouble, but receives its array argument using keywordref. In this case, the reference stored insecondArrayafter the method call is a reference to the array created in line 99 ofSecondDouble, demonstrating that a variable passed with key-
Test passing firstArray reference by value
Contents of firstArray before calling FirstDouble:
1 2 3
Contents of firstArray after calling FirstDouble 2 4 6
The references refer to the same array
Test passing secondArray reference by reference Contents of secondArray before calling SecondDouble:
1 2 3
Contents of secondArray after calling SecondDouble:
11 12 13
The references refer to different arrays
Fig. 8.14 | Passing an array reference by value and by reference. (Part 3 of 3.)
8.9 Case Study:GradeBookUsing an Array to Store Grades 311
wordrefcan be modified by the called method so that the variable in the caller actually points to adifferentobject—in this case, an array created inSecondDouble. Theif...else statement in lines 71–76 confirms thatsecondArrayandsecondArrayCopyno longer re- fer to the same array.