Передача массивов по значению и по ссылке
Это пример из книги С#, которую я читаю, просто испытывая небольшую неприятность, понимая, что этот пример на самом деле делает, хотел бы объяснить объяснение, чтобы помочь мне понять, что здесь происходит.
//creates and initialzes firstArray
int[] firstArray = { 1, 2, 3 };
//Copy the reference in variable firstArray and assign it to firstarraycopy
int[] firstArrayCopy = firstArray;
Console.WriteLine("Test passing firstArray reference by value");
Console.Write("\nContents of firstArray " +
"Before calling FirstDouble:\n\t");
//display contents of firstArray with forloop using counter
for (int i = 0; i < firstArray.Length; i++)
Console.Write("{0} ", firstArray[i]);
//pass variable firstArray by value to FirstDouble
FirstDouble(firstArray);
Console.Write("\n\nContents of firstArray after " +
"calling FirstDouble\n\t");
//display contents of firstArray
for (int i = 0; i < firstArray.Length; i++)
Console.Write("{0} ", firstArray[i]);
// test whether reference was changed by FirstDouble
if (firstArray == firstArrayCopy)
Console.WriteLine(
"\n\nThe references refer to the same array");
else
Console.WriteLine(
"\n\nThe references refer to different arrays");
//method firstdouble with a parameter array
public static void FirstDouble(int[] array)
{
//double each elements value
for (int i = 0; i < array.Length; i++)
array[i] *= 2;
//create new object and assign its reference to array
array = new int[] { 11, 12, 13 };
В основном есть код, который я хотел бы знать, так это то, что в книге говорится, что массив передается по значению, чем исходный вызывающий, который не модифицируется методом (из того, что я понимаю). Таким образом, к концу метода FirstDouble они пытаются назначить локальный массив переменных новому набору элементов, который терпит неудачу, а новые значения исходного вызывающего абонента при отображении - 2,4,6.
Теперь моя путаница в том, как цикл for в методе FirstDouble изменял первоначальный вызывающий firstArray до 2,4,6, если он был передан по значению. Я думал, что стоимость должна оставаться 1,2,3.
Заранее спасибо
Ответы
Ответ 1
Ключом к пониманию этого является знать разницу между типом значения и ссылочным типом.
Например, рассмотрим типичный тип значения int
.
int a = 1;
int b = a;
a++;
После выполнения этого кода a
имеет значение 2, а b
имеет значение 1
. Поскольку int
- тип значения, b = a
берет копию значения a
.
Теперь рассмотрим класс:
MyClass a = new MyClass();
a.MyProperty = 1;
MyClass b = a;
a.MyProperty = 2;
Поскольку классы являются ссылочными типами, b = a
просто присваивает ссылку, а не значение. Поэтому b
и a
оба относятся к одному и тому же объекту. Следовательно, после a.MyProperty = 2
выполняется b.MyProperty == 2
, так как a
и b
относятся к одному и тому же объекту.
Учитывая код в вашем вопросе, массив является ссылочным типом, поэтому для этой функции:
public static void FirstDouble(int[] array)
переменная array
на самом деле является ссылкой, потому что int[]
является ссылочным типом. Таким образом, array
является ссылкой, которая передается по значению.
Таким образом, изменения, внесенные в array
внутри функции, фактически применяются к объекту int[]
, к которому относится array
. И поэтому эти модификации видны всем ссылкам, относящимся к тому же самому объекту. И это включает ссылку, которую имеет вызывающий абонент.
Теперь, если мы посмотрим на реализацию этой функции:
public static void FirstDouble(int[] array)
{
//double each elements value
for (int i = 0; i < array.Length; i++)
array[i] *= 2;
//create new object and assign its reference to array
array = new int[] { 11, 12, 13 };
}
есть еще одно осложнение. Цикл for
просто удваивает каждый элемент int[]
, который передается функции. Это изменение, которое видит вызывающий. Вторая часть - это назначение нового объекта int[]
локальной переменной array
. Это не видно вызывающему, потому что все, что он делает, это изменить цель ссылки array
. А поскольку ссылка array
передается по значению, вызывающий объект не видит этот новый объект.
Если функция была объявлена следующим образом:
public static void FirstDouble(ref int[] array)
тогда ссылка array
была бы передана по ссылке, и вызывающая сторона увидела вновь созданный объект { 11, 12, 13 }
, когда функция вернулась.
Ответ 2
Что за запутанное использование терминов!
Чтобы уточнить,
для метода foo(int[] myArray)
"передача ссылки (объекта) по значению" фактически означает "передачу копии адреса объекта (ссылки)". Значение этой "копии", т.е. myArray
- это изначально адрес (ссылка) исходного объекта, то есть он указывает на исходный объект. Следовательно, любое изменение содержимого, на которое указывает myArray
, повлияет на содержимое исходного объекта.
Однако, поскольку "значение" самого myArray
является копией, любое изменение этого "значения" не повлияет на исходный объект и его содержимое.
для метода foo(ref int[] refArray)
"передача ссылки (объекта) по ссылке" означает "передачу адреса объекта (ссылки) самой (не копии)". Это означает, что refArray
на самом деле является исходным адресом самого объекта, а не его копией. Следовательно, любое изменение "значения" refArray
или содержимого, на которое указывает refArray
, является прямым изменением самого исходного объекта.
Ответ 3
Все параметры метода передаются по значению, если вы специально не видите ref
или out
.
Массивы являются ссылочными типами. Это означает, что вы передаете ссылку по значению.
Сама эта ссылка изменяется только тогда, когда вы назначаете ей новый массив, поэтому эти назначения не отражаются в вызывающем. Когда вы удаляете ссылку на объект (массив здесь) и изменяете базовое значение, которое вы не меняете переменной, просто то, на что оно указывает. Это изменение будет также "видно" вызывающим, хотя переменная (то есть то, на что она указывает) остается постоянной.