List passed by ref - help me explain this behaviour

C#ListPass by-Reference

C# Problem Overview


Take a look at the following program:

class Test
{
    List<int> myList = new List<int>();

    public void TestMethod()
    {
        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList(myList);

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList(List<int> myList)
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

I assumed myList would have passed by ref, and the output would

3
4

The list is indeed "passed by ref", but only the sort function takes effect. The following statement myList = myList2; has no effect.

So the output is in fact:

10
50
100

Can you help me explain this behavior? If indeed myList is not passed-by-ref (as it appears from myList = myList2 not taking effect), how does myList.Sort() take effect?

I was assuming even that statement to not take effect and the output to be:

100
50
10

C# Solutions


Solution 1 - C#

Initially, it can be represented graphically as follow:

Init states

Then, the sort is applied myList.Sort(); Sort collection

Finally, when you did: myList' = myList2, you lost the one of the reference but not the original and the collection stayed sorted.

Lost reference

If you use by reference (ref) then myList' and myList will become the same (only one reference).

Note: I use myList' to represent the parameter that you use in ChangeList (because you gave the same name as the original)

Solution 2 - C#

You are passing a reference to the list, but your aren't passing the list variable by reference - so when you call ChangeList the value of the variable (i.e. the reference - think "pointer") is copied - and changes to the value of the parameter inside ChangeList aren't seen by TestMethod.

try:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

This then passes a reference to the local-variable myRef (as declared in TestMethod); now, if you reassign the parameter inside ChangeList you are also reassigning the variable inside TestMethod.

Solution 3 - C#

Here is an easy way to understand it

  • Your List is an object created on heap. The variable myList is a reference to that object.

  • In C# you never pass objects, you pass their references by value.

  • When you access the list object via the passed reference in ChangeList (while sorting, for example) the original list is changed.

  • The assignment on the ChangeList method is made to the value of the reference, hence no changes are done to the original list (still on the heap but not referenced on the method variable anymore).

Solution 4 - C#

This link will help you in understanding pass by reference in C#. Basically,when an object of reference type is passed by value to an method, only methods which are available on that object can modify the contents of object.

For example List.sort() method changes List contents but if you assign some other object to same variable, that assignment is local to that method. That is why myList remains unchanged.

If we pass object of reference type by using ref keyword then we can assign some other object to same variable and that changes entire object itself.

(Edit: this is the updated version of the documentation linked above.)

Solution 5 - C#

C# just does a shallow copy when it passes by value unless the object in question executes ICloneable (which apparently the List class does not).

What this means is that it copies the List itself, but the references to the objects inside the list remain the same; that is, the pointers continue to reference the same objects as the original List.

If you change the values of the things your new List references, you change the original List also (since it is referencing the same objects). However, you then change what myList references entirely, to a new List, and now only the original List is referencing those integers.

Read the Passing Reference-Type Parameters section from this MSDN article on "Passing Parameters" for more information.

"How do I Clone a Generic List in C#" from StackOverflow talks about how to make a deep copy of a List.

Solution 6 - C#

Use the ref keyword.

Look at the definitive reference here to understand passing parameters.
To be specific, look at this, to understand the behavior of the code.

EDIT: Sort works on the same reference (that is passed by value) and hence the values are ordered. However, assigning a new instance to the parameter won't work because parameter is passed by value, unless you put ref.

Putting ref lets you change the pointer to the reference to a new instance of List in your case. Without ref, you can work on the existing parameter, but can't make it point to something else.

Solution 7 - C#

While I agree with what everyone has said above. I have a different take on this code. Basically you're assigning the new list to the local variable myList not the global. if you change the signature of ChangeList(List myList) to private void ChangeList() you'll see the output of 3, 4.

Here's my reasoning... Even though list is passed by reference, think of it as passing a pointer variable by value When you call ChangeList(myList) you're passing the pointer to (Global)myList. Now this is stored in the (local)myList variable. So now your (local)myList and (global)myList are pointing to the same list. Now you do a sort => it works because (local)myList is referencing the original (global)myList Next you create a new list and assign the pointer to that your (local)myList. But as soon as the function exits the (local)myList variable is destroyed. HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}


Solution 8 - C#

There are two parts of memory allocated for an object of reference type. One in stack and one in heap. The part in stack (aka a pointer) contains reference to the part in heap - where the actual values are stored.

When ref keyword is not use, just a copy of part in stack is created and passed to the method - reference to same part in heap. Therefore if you change something in heap part, those change will stayed. If you change the copied pointer - by assign it to refer to other place in heap - it will not affect to origin pointer outside of the method.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questioncoder_broView Question on Stackoverflow
Solution 1 - C#JaiderView Answer on Stackoverflow
Solution 2 - C#Marc GravellView Answer on Stackoverflow
Solution 3 - C#Unmesh KondolikarView Answer on Stackoverflow
Solution 4 - C#ShekharView Answer on Stackoverflow
Solution 5 - C#Ethel EvansView Answer on Stackoverflow
Solution 6 - C#shahkalpeshView Answer on Stackoverflow
Solution 7 - C#sandeepView Answer on Stackoverflow
Solution 8 - C#Thinh TranView Answer on Stackoverflow