Передача массивов по значению и по ссылке

Это пример из книги С#, которую я читаю, просто испытывая небольшую неприятность, понимая, что этот пример на самом деле делает, хотел бы объяснить объяснение, чтобы помочь мне понять, что здесь происходит.

        //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

Что за запутанное использование терминов!

Чтобы уточнить,

  1. для метода foo(int[] myArray) "передача ссылки (объекта) по значению" фактически означает "передачу копии адреса объекта (ссылки)". Значение этой "копии", т.е. myArray - это изначально адрес (ссылка) исходного объекта, то есть он указывает на исходный объект. Следовательно, любое изменение содержимого, на которое указывает myArray, повлияет на содержимое исходного объекта.

    Однако, поскольку "значение" самого myArray является копией, любое изменение этого "значения" не повлияет на исходный объект и его содержимое.

  2. для метода foo(ref int[] refArray) "передача ссылки (объекта) по ссылке" означает "передачу адреса объекта (ссылки) самой (не копии)". Это означает, что refArray на самом деле является исходным адресом самого объекта, а не его копией. Следовательно, любое изменение "значения" refArray или содержимого, на которое указывает refArray, является прямым изменением самого исходного объекта.

Ответ 3

Все параметры метода передаются по значению, если вы специально не видите ref или out.

Массивы являются ссылочными типами. Это означает, что вы передаете ссылку по значению.

Сама эта ссылка изменяется только тогда, когда вы назначаете ей новый массив, поэтому эти назначения не отражаются в вызывающем. Когда вы удаляете ссылку на объект (массив здесь) и изменяете базовое значение, которое вы не меняете переменной, просто то, на что оно указывает. Это изменение будет также "видно" вызывающим, хотя переменная (то есть то, на что она указывает) остается постоянной.