1

So I've got a simple string array. Passed it to a function that reverses its input and then displayed the content of the array. I was expecting the contents of the array to reverse since arrays are passed by reference, but the string array did not change.

string[] words = { "Metal", "Gear", "is", "Awesome!" };

mutateArray(ref words);

foreach (string word in words)
     Console.Write(word + " "); 

This is my mutateArray function:

 public static void mutateArray(ref string[] arr)
 {
      arr = arr.Reverse().ToArray();          
 }

I know that the mutateArray method changes to the array will persist once I state that the parameter must be passed in with the keyword ref.

  1. Aren't all arrays passed in by reference by default?
  2. Why do the changes persist when the keyword ref is involved?
  3. What's the difference between passing a reference type (classes, interfaces, array, delegates) by value vs passing them by reference (with the keyword ref)?
Ani
  • 2,636
  • 1
  • 21
  • 17
Mustafa
  • 177
  • 1
  • 2
  • 10
  • So you're asking how does `ref` work, and whats the difference between passing value types and reference types as arguments. Answers to all those questions already exist in SO. – dcastro Jun 18 '14 at 13:15
  • 3
    Basically, read http://pobox.com/~skeet/csharp/parameters.html – Jon Skeet Jun 18 '14 at 13:15
  • why would you downvote a well-formed question? – Jonesopolis Jun 18 '14 at 13:18
  • this works on linqpad – NeddySpaghetti Jun 18 '14 at 13:18
  • According to the MSDN Documentation, this should work. Are you sure you have included all of the details? Here's the documentation I was referring to: http://msdn.microsoft.com/en-us/library/14akc2c7.aspx – Justin Niessner Jun 18 '14 at 13:19
  • http://stackoverflow.com/questions/18787530/passing-reference-type-in-c-sharp/18787632#18787632 – Habib Jun 18 '14 at 13:20
  • @dcastro I've said nothing about value types. I'm specifically talking about arrays and reference values which are to my understanding passed in by reference. I also did not ask about how does ref work, But in this specific example why is it that adding ref keyword outputted the expected result. Did you even read the question ? – Mustafa Jun 18 '14 at 13:20
  • @Mustafa: Your understanding is flawed, because passing a reference by value is not the same as passing an object by reference. Again, read my article... it should help to clear things up for you. – Jon Skeet Jun 18 '14 at 13:22
  • @NedStoyanov What works exactly? – Mustafa Jun 18 '14 at 13:23
  • "I also did not ask about how does ref work, But in this specific example why is it that adding ref keyword outputted the expected result." If you knew how ref works, you'd know why adding it outputted the expected result. So yes, the two questions are essentially the same. – dcastro Jun 18 '14 at 13:40
  • @Mustafa your sample code works. It reverses the array and it prints `Awesome! is Gear Metal` – NeddySpaghetti Jun 18 '14 at 13:43
  • 1
    @NedStoyanov, he knows the posted code works. He's asking why it *didn't* work until he added `ref`. – adv12 Jun 18 '14 at 13:46
  • @adv12 one of the earlier answers said it wasn't going to work because of the ToArray call, so I just wanted to point out that it works – NeddySpaghetti Jun 18 '14 at 14:10

5 Answers5

2

The reason this doesn't work how you expect is that Reverse() does not actually reverse the contents of the array in place but rather makes a new list with the reversed contents of the original. That's why it works once you pass the array by reference: then, you're actually replacing the entire original array in the calling method with a new one created in mutateArray.

If you had a method that did the reversing in-place, you could pass in the original array (not using ref), and after the method call, the array would be in reverse order.

adv12
  • 8,443
  • 2
  • 24
  • 48
  • 1+ Did not know that Reverse() operates on a new list instead of the original one. Thank you :) – Mustafa Jun 18 '14 at 13:37
1
  1. You're confusing a Reference Type with passing by Reference. The ref keyword can be applied to both Value and Reference types. Even Reference types aren't passed by reference by default. Instead the reference is passed by value to the method.

  2. Based on the documentation from MSDN, they should. That's the whole purpose of using the ref keyword with a Reference Type.

  3. The difference is that when you pass by Reference Type by Reference, you are able to change the reference of the original variable rather than just the instance inside your method. Check the previously linked documentation for more details.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • I understand the difference between the type and the passing by reference, but I think that my C++ knowledge is getting in my way. As far as I know, in C++ there's either a pass by reference or by value .. Wait, as I'm writing this I realize it's basically similar and I was indeed confused by the word Reference in the type +1:) – Mustafa Jun 18 '14 at 13:52
  • In c++ terms I think of a c# reference being passed to a method kind of like a c++ pointer being passed in, if you want to change the pointer you need to pass in a reference to a pointer or a pointer to a pointer. – NeddySpaghetti Jun 18 '14 at 14:07
1
  1. All parameters are passed by value by default in C#. For reference types like array, this means the reference is passed by value.

  2. ref causes the variable to be passed by reference into the function. This effectively means the arr parameter in mutateArray is an alias for words in the caller. This is why an assignment to arr results in a change in words after mutateArray has exited.

  3. Passing a reference type by value into a function means a copy of the reference is made. Without the ref modifier, arr in mutateArray is a different variable containing a reference to the same object as words in the caller. Assigning to arr in this case has no effect on words in the caller. Note that you can mutate the array through the shared reference, but arr and words are separate storage locations.

Lee
  • 142,018
  • 20
  • 234
  • 287
  • +1 "Without the ref modifier, arr in mutateArray is a different variable containing a reference to the same object as words in the caller". This sentence cleared things up for me :) – Mustafa Jun 18 '14 at 13:31
  • I feel this might be a stupid question but, since the arr in the mutateArray function is still pointing to the same object, Why can't I see these changes through the original reference. It's the same object after all. Is it because the changes to the object are revoked once the function finish executing ?! – Mustafa Jun 18 '14 at 13:35
  • @Mustafa - They start out referring to the same object, but they are different variables. You are assigning to one variable and expecting it to be reflected in another independent variable. The `ref` modifier makes `arr` an `alias` for `words` and in that case the assignment inside the function is visible once it has exited. – Lee Jun 18 '14 at 13:44
0

words is a reference to an array. Just consider it to contain the memory address of that array.

When you give it to MutateArray as a parameter (without the ref keyword) its VALUE will be copied into arr. So arr is a different variable as words, but they contain the same value (= memory address). This means that they refer to the same object (the string array).

You can change the contents of the object, but words (and arr) will still be referring to it.

If you assign arr to a different object, then its value changes, so it will refer to a different object than words.

However, if you use the ref keyword, then arr and words are the SAME variable. That means if you change arr's value (= assign it to a new object), you are also changing words's value, so words will refer to the same, new, object.

Maybe all of this is not technically 100% correct, but it's the way I like to think about it in order to understand how it works.

The ref keyword is why the following Swap method works in C#; without it, it would just change the inner variables of the Swap method (and do nothing basicallly)

public void Swap<T>(ref T a, ref T b) {
    T temp = a;
    a = b;
    b = temp;
}
Dennis_E
  • 8,751
  • 23
  • 29
0

Your question is totally understandable and for a moment I was confused as well.
So here is the explanation.

Short explanation

  • When you pass a reference type to a method and make a change to its property, the object outside the method CAN SEE the property change because the object itself remains the same.
  • When you pass a reference type to a method and make a change to the instance itself, the object outside the method CANNOT SEE the change because inside the method you basically started pointing to another object. So the object changed inside the method and remained there as a stranger.

Long explanation

  • Suppose you have a reference type instance where you get the value from database.

        using (var context = new MyAdventureWorksEntities2())
        {
            Product p = context.Products.Where(item => item.ProductID == 1000).First();
            Console.WriteLine(p.Name); // p.Name = "INITIAL NAME"
            UpdateName(p);
            Console.WriteLine(p.Name);
        }
    

And here is your UpdateName method:

    public static void UpdateName(Product p)
    {
        p.Name = "UPDATED NAME";
    }

This code emits the following result:
INITIAL NAME
UPDATED NAME

  • HOWEVER, if you change the method to the following:

    public static void UpdateName(Product p)
    {
        using (var context = new MyAdventureWorksEntities2())
        {
            p = context.Products.Where(item => item.ProductID == 1003).First();
            // p.Name = "ANOTHER PRODUCT NAME"
        }
    }
    

your result will be:
INITIAL NAME
INITIAL NAME

Note that I didn't touch the ref keyword at all.
And perhaps after those examples the short description will be much more comprehensible.

Ani
  • 2,636
  • 1
  • 21
  • 17