Algo
Algo

Reputation: 198

C# Why does the argument array has to be passed with the modifier ref if arrays are passed by reference?

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)

Upvotes: 3

Views: 562

Answers (3)

Oleksandr Tyshchenko
Oleksandr Tyshchenko

Reputation: 471

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

Upvotes: 1

Stuart
Stuart

Reputation: 5506

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.

Upvotes: 6

Sweeper
Sweeper

Reputation: 273380

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.

Upvotes: 3

Related Questions