39

These are example from a c# book that I am reading just having a little trouble grasping what this example is actually doing would like an explanation to help me further understand what is happening here.

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

Basically there is the code what I would like to know is that the book is saying if the array is passed by value than the original caller does not get modified by the method(from what i understand). So towards the end of method FirstDouble they try and assign local variable array to a new set of elements which fails and the new values of the original caller when displayed are 2,4,6.

Now my confusion is how did the for loop in method FirstDouble modify the original caller firstArray to 2,4,6 if it was passed by value. I thought the value should remain 1,2,3.

Thanks in advance

svick
  • 236,525
  • 50
  • 385
  • 514
Tim
  • 1,209
  • 4
  • 21
  • 33
  • possible duplicate of [Value type and reference type problem](http://stackoverflow.com/questions/6070892/value-type-and-reference-type-problem) – Alexei Levenkov Apr 25 '12 at 23:50
  • @AlexeiLevenkov With this covered so well elsewhere, I'd hate to close for that one :( –  Apr 26 '12 at 00:10

4 Answers4

70

The key to understanding this is to know the difference between a value type and a reference type.

For example, consider a typical value type, int.

int a = 1;
int b = a;
a++;

After this code has executed, a has the value 2, and b has the value 1. Because int is a value type, b = a takes a copy of the value of a.

Now consider a class:

MyClass a = new MyClass();
a.MyProperty = 1;
MyClass b = a;
a.MyProperty = 2;

Because classes are reference types, b = a merely assigns the reference rather than the value. So b and a both refer to the same object. Hence, after a.MyProperty = 2 executes, b.MyProperty == 2 since a and b refer to the same object.


Considering the code in your question, an array is a reference type and so for this function:

public static void FirstDouble(int[] array)

the variable array is actually a reference, because int[] is a reference type. So array is a reference that is passed by value.

Thus, modifications made to array inside the function are actually applied to the int[] object to which array refers. And so those modifications are visible to all references that refer to that same object. And that includes the reference that the caller holds.

Now, if we look at the implementation of this function:

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 };
}

there is one further complication. The for loop simply doubles each element of the int[] that is passed to the function. That's the modification that the caller sees. The second part is the assignment of a new int[] object to the local variable array. This is not visible to the caller because all it does is to change the target of the reference array. And since the reference array is passed by value, the caller does not see that new object.

If the function had been declared like this:

public static void FirstDouble(ref int[] array)

then the reference array would have been passed by reference and the caller would see the newly created object { 11, 12, 13 } when the function returned.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • So what is the difference from doing `public static void FirstDouble(ref int[] array)`? – Jmyster Apr 25 '12 at 23:46
  • No It is wrong to say "the variable array is actually a reference". That would mean that assigning to it would change the value in the caller when using call-by-reference terminology. Keeping up this confusing term of "reference" makes it hard to talk about references in C++ or `ref/out` in C#, which is why it gets my -1. –  Apr 25 '12 at 23:55
  • 1
    @pst, maybe it's confusing, but it's not wrong. There is a reason why reference types are called that. – svick Apr 25 '12 at 23:57
  • 1
    @svick The wording is wrong *because* it is confusing. To say "x is a reference" is not the same as to say "typeof(x) is a reference type". **The variable is not a reference**. One needs to separate the semantics of a variable and the semantics of the value (that the variable uses to "reference the object"). –  Apr 25 '12 at 23:57
  • @pst I can't see where C++ comes into this. – David Heffernan Apr 26 '12 at 00:00
  • @DavidHeffernan In the same way `ref/out` does. It is not possible to correctly say, with `ref int[] array`, that `array` is a reference (where assignment to it will affect the variable in the caller) because it has already been used to describe array in `int[] array`: "variable array is actually a reference". So then **what is** it in the second case? It *has* to be the same (so it will fit the call-by-reference definition) but it *can't* be the same because of the previous wording which covers a *different* use-case. –  Apr 26 '12 at 00:02
  • That is, it is *not* the variable that is a reference in `int[] array`. Rather, a variable of a reference-type only "stores the reference to an object" (I prefer to simply use the wording "names an object", but that's a different conversation ;-). Of course C# hides this detail in calling semantics and usage. –  Apr 26 '12 at 00:05
  • @DavidHeffernan C++ is different in that the terminology is more precise. In C# you can pass an object by value or by reference, and you can have a type that is a value type or a reference type. In C++ all objects are values, they can be passed by value or reference, but you use a pointer to have a variable that references an object. Therefore there is a distinction between the type of reference to an object and the semantics are a bit clearer when described in English. – Servy Apr 26 '12 at 00:14
  • @David thanks for the response your explanation helps but still confused I was under the impression that by not specifying "ref" or "out" that the default would be passed "byvalue" which in return would not modify the original caller. And if the variable is a reference type passed by value why is the new int[] object array {11,12,13} not applied to the int[] reference type? Sorry just trying to understand this before I move on to the next section – Tim Apr 26 '12 at 00:48
  • "class" objects (such as an int[] array object) are always passed by reference. Always. There's no concept of passing them "by value" (unless you implement some sort of cloning strategy). "struct" objects (such as int, bool, double) always pass by value unless you specify "ref" or "out" in which case they pass by reference. (but as mentioned, "ref" and "out" are special usages and don't behave exactly the same as passing a "class" object does) – Chris Sinclair Apr 26 '12 at 01:13
  • The reference to the object is passed by value – David Heffernan Apr 26 '12 at 01:45
  • Thanks for the help all appreciate it – Tim Apr 26 '12 at 01:45
  • @pst "It is not possible to correctly say, with `ref int[] array`, that `array` is a reference". In that situation I think of it that the reference is passed by reference. – David Heffernan Apr 26 '12 at 02:08
  • And to think, that's actually the only wording in the post I find wrong/incorrect... once again, my argument is that [normal] *variables are not references*. The value then "contain" may be a reference. This is *not* to say the variable is a reference, as doing so implies that re-assignment *could* have non-local effects. (If there were *not* implied about a "reference variable" then there would be no way to talk about `ref int[] array`.) –  Apr 26 '12 at 03:52
  • 2
    @David Well after reading your response over and over again I finally understand what you are saying. That was confusing but thanks for your detailed explanation it really helped – Tim Apr 26 '12 at 10:24
  • Great explanation!! – Antiohia Oct 13 '17 at 11:36
  • @DavidHeffernan is this your blog? https://newbedev.com/passing-arrays-by-value-and-by-reference :) – Avantha Siriwardana Dec 06 '21 at 10:55
20

What a confusing use of terms!

To clarify,

  1. for a method foo(int[] myArray), "passing a reference (object) by value" actually means "passing a copy of the object's address (reference)". The value of this 'copy', ie. myArray, is initially the Address (reference) of the original object, meaning it points to the original object. Hence, any change to the content pointed to by myArray will affect the content of the original object.

    However, since the 'value' of myArray itself is a copy, any change to this 'value' will not affect the original object nor its contents.

  2. for a method foo(ref int[] refArray), "passing a reference (object) by reference" means "passing the object's address (reference) itself (not a copy)". That means refArray is actually the original address of the object itself, not a copy. Hence, any change to the 'value' of refArray, or the content pointed to by refArray is a direct change on the original object itself.

Community
  • 1
  • 1
user1570129
  • 201
  • 2
  • 2
8

All method parameters are passed by value unless you specifically see ref or out.

Arrays are reference types. This means that you're passing a reference by value.

The reference itself is only changed when you assign a new array to it, which is why those assignments aren't reflected in the caller. When you de-reference the object (the array here) and modify the underlying value you aren't changing the variable, just what it points to. This change will be "seen" by the caller as well, even though the variable (i.e. what it points to) remains constant.

Servy
  • 202,030
  • 26
  • 332
  • 449
0

idea for you all there with knowledge of .net open sources to implement the logics;

//Sample Code, Illustration;
Method1(params dynamic[] var1) {
  var1[0]=new dynamic[3] { 1,2,3 }
}

the var1 is not specified or cannot be ref ? a usage scenario would be ...

//Sample Code, Illustration;
dynamic[] test = new dynamic[];
Method1( ref test,x,x,x,x  );
System.Windows.MessageBox.Show( test[2].ToString() );

to indicate ref only when, not being a parameter specific; and a ref to array items;

//result is IndexOutOfBounds;

this is only a illustrationm it can be done by returning a array and use like:

test = Method1( test,... );

instead of :

Method1( ref test,x,x,..., ref test[x], ref test2, ... );
H3sDW11e
  • 188
  • 1
  • 3
  • 11