3

I do not understand why this function was written like that:

System.Array.Resize<int>(ref int[], int)

If arrays are passed by reference by default why wasn't it written like that:

System.Array.Resize<int>(int[], int)
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Algo
  • 178
  • 7
  • 3
    They are not. An array is a reference type, passing by reference is something different – UnholySheep Dec 01 '19 at 18:30
  • Does this answer your question? [Passing Objects By Reference or Value in C#](https://stackoverflow.com/questions/8708632/passing-objects-by-reference-or-value-in-c-sharp) – UnholySheep Dec 01 '19 at 18:32
  • If the Resize method succeeds then variable will point to a different array - the resized one. This is because with ref parameter you will pass reference to an array reference so in the method body you can modify the array reference itself. With regular parameters this is not possible - reference to an array is passed (by copying) so even if you modify it in the method body you will operate on the copied reference, not the original one. – jacekbe Dec 01 '19 at 18:39
  • 1
    See also: https://jonskeet.uk/csharp/parameters.html – Jon Skeet Dec 01 '19 at 19:16

3 Answers3

6

This is because when we write a variable to a reference type object, there are kind of 2 parts, the actual object instance, and the reference which the variable name represents (32bit or 64bit memory address pointer internally, platform dependant). You can see that clearly with this sharplab.io snippet.

When we call a method, this pointer is copied, but the instance isn't, so:

var a = new Blah {Prop = "1"}; // Blah is a class, a reference type
Method(a);

void Method(Blah blah)
{
    blah.Prop = "2"; // changes the shared instance, we know this.

    blah = new Blah {Prop = "3"}; // has no effect.
}

Console.WriteLine(a.Prop); // 2

You see when we set blah inside of the method, we are mutating our local reference, not the shared one. Now if we use the ref keyword:

var a = new Blah {Prop = "1"};
Method(ref a);

void Method(ref Blah blah)
{
    blah.Prop = "2"; // changes the shared instance, we know this.

    blah = new Blah {Prop = "3"}; // now changes ref a outside
}

Console.WriteLine(a.Prop); // 3!

because the parameter blah is passed by reference, when we mutate it, we mutate the original reference a.

Stuart
  • 5,358
  • 19
  • 28
3

Arrays are indeed reference types, which means that changes done to the passed-in array object inside a method will be reflected on the caller's side:

public static void Foo(int[] a) {
    a[0] = 1;
}

// ...
int[] a = new int[1];
Foo(a);
Console.WriteLine(a[0]); // 1

However, if you set the array to something else inside the method:

public static void Foo(int[] a) {
    a = null;
}

// ...
int[] a = new int[1];
Foo(a);
Console.WriteLine(a[0]); // will not throw NRE

Declaring the parameter as ref will allow reassignments to the parameter to reflect on the caller's side.

Changing the size of the array requires creating a new array and hence re-assigning the parameter. You can't resize an array by mutating an existing array object somehow. This is why it needs to be declared as ref.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
1

Simply speaking if your array is passed to a method as a ref parameter it can be replaced as a whole with another array created within the method. It's not the case with arrays passed without ref keyword. Code below illustrates the difference. Note that individual elements of array parameters can be replaced in both cases (with of without ref keyword).

class Program
{
    static void PrintArr(string comment, int[] arr)
    {
        Console.WriteLine($"{comment}:");
        Console.WriteLine(string.Join(", ", arr.Select(e => e.ToString())));
    }
    static void NoRef(int[] arr)
    {
        int[] localArr = { 2, 4, 8, 10 };
        arr = localArr;
    }
    static void ByRef(ref int[] arr)
    {
        int[] localArr = { 2, 4, 8, 10 };
        arr = localArr;
    }

    static void Main()
    {
        int[] arr;
        arr = new int[] { 1, 3, 4, 7, 9 };

        PrintArr("Before NoRef is called", arr);
        NoRef(arr);
        PrintArr("After NoRef is called", arr);

        PrintArr("Before ByRef is called", arr);
        ByRef(ref arr);
        PrintArr("After ByRef is called", arr);

        Console.ReadLine();
    }
}

}

Output for the code is shown below (note that ByRef method code replaced the array.

Before NoRef is called:

1, 3, 4, 7, 9

After NoRef is called:

1, 3, 4, 7, 9

Before ByRef is called:

1, 3, 4, 7, 9

After ByRef is called:

2, 4, 8, 10