-2

So i ran into this bug where C# behaves like i passed in a list by reference and not by value, like it usually does. Let me show an example:

using System;
using System.Collections.Generic;

namespace testprogram
{
    class Program
    {
        static int x;
        static List<Coordinate> coordinates;

        static void Main(string[] args)
        {
            x = 10;
            coordinates = new List<Coordinate>();
            coordinates.Add(new Coordinate(0, 0));
            coordinates.Add(new Coordinate(1, 1));

            testfunction(x, coordinates);
            Console.WriteLine(x);
            foreach (var objekt in coordinates)
            {
                Console.WriteLine(objekt.xpos);
                Console.WriteLine(objekt.ypos);
            }
            Console.Read();
        }

        static void testfunction(int test, List<Coordinate> objects)
        {
            test = 4;

            foreach (Coordinate obj in objects)
            {
                obj.xpos = 4;
                obj.ypos = 4;
            }
        }


    }

    class Coordinate
    {
        public int xpos;
        public int ypos;

        public Coordinate(int new_x, int new_y)
        {
            xpos = new_x;
            ypos = new_y;
        }
    }
}

This code outputs:

10 
4
4
4
4

But why? I expected it to be:

10
0
0
1
1

I tried to make an extra List in the Funktion and assign the value of the parameter to it but even that didn´t work. Is there some workaround for this?

  • 1
    Primitive types (int) are passed by value. Objects (e.g List) is passed by reference... See C# 101 preface... – Roland Buergi Aug 10 '16 at 22:10
  • List is inherently a reference type, while your int is not. Both are passed by value, but for a list that copied value is a (new) pointer, pointing to the SAME underlying list. So the List values really are updated, whereas the int is not. – SlimsGhost Aug 10 '16 at 22:11
  • I think this is a good beginner question. I don't think minus votes are appropriate here. I vote the question +1. – Al Kepp Aug 10 '16 at 22:14
  • In. NET, some types are always passed by reference. The List class, as well as your Coordinate class, would behave this way. You'll need to look up Value Types and Reference Types to understand why. – Biscuits Aug 10 '16 at 22:16
  • 1
    @Biscuits - your statement is incorrect. Types are only passed by reference is you use the `ref` or `out` keyword. Reference types will be passed by value (i.e. the reference itself will be passed as a value, not the object that the reference is pointing to). See the comment that I added to my answer. – LeopardSkinPillBoxHat Aug 10 '16 at 22:38
  • @LeopardSkinPillBoxHat Apologies, that came out all wrong. Not sure how to say it simply, though. – Biscuits Aug 10 '16 at 22:42

3 Answers3

1

The List<T> argument is a reference type which is passed by value.

If you modify any items in the list within the method, these will remain changed when you leave the method.

However, if you reassign the reference inside the method, these changes will be lost outside the method. If you want to pass a parameter by reference, you use the ref keyword:

static void testfunction(int test, ref List<Coordinate> objects)
{
    // This will update objects outside the testfunction method
    objects = new List<Coordinate>();
}

You can also use the out keyword, which works similar to the ref keyword. The only difference is that you don't have to initialise values that are passed in as out parameters before you call the method, and they must be initialised before leaving the method.

static void Main(string[] args)
{
    // No need to initialise variable passed as "out" parameter
    List<Coordinate> objects;
    testfunction(test, out objects);
}

static void testfunction(int test, out List<Coordinate> objects)
{
    // Removing this line would result in a compilation error
    objects = new List<Coordinate>();
}
LeopardSkinPillBoxHat
  • 28,915
  • 15
  • 75
  • 111
  • @Dispersia - You can read the difference between reference and value types [here](http://stackoverflow.com/questions/5057267/what-is-the-difference-between-a-reference-type-and-value-type-in-c). Think of a reference like a pointer to a value stored elsewhere. When I say a reference tpye is passed by value, it means that the "pointer" value is passed in, and if that "pointer" value is updated within the method (e.g. a new object is created, or it's pointed to another object) this change won't be seen outside the method. – LeopardSkinPillBoxHat Aug 10 '16 at 22:24
0

You passed a reference to the list "by value". So, if you change the reference "object" in "testfunction", then "coordinates" will not change (as a pointer). But changing the elements of "object" will affect the elements of "coordinates".

AhmadWabbi
  • 2,253
  • 1
  • 20
  • 35
0

Your expectations are wrong. The list is passed as a value. It means that variable named objects is a value-copy of original list variable. But the list contains references, I mean the contents of the list are references to Coordinate objects. So if you would try to change the list variable like objects = new List<>(), it wouldn't change your original list. But if you change the objects inside the list, the changes are actually applied to the original list.

objects[0] = new Coordinate(5, 5);
objects[0].xpos = 6;

Both of these examples change the original list.

If you want to have the possibility to safely change anything in the list, you have to make a deep clone of it. You can use Clone() method, but it can be tricky, because you must manually clone each object inside the list. This was already answered here: How create a new deep copy (clone) of a List<T>?

Community
  • 1
  • 1
Al Kepp
  • 5,831
  • 2
  • 28
  • 48
  • Ok that makes sense. But how can i workaround this. Maybe a way to duplicate a list and its objects or something? – HarryGoatleaf Aug 10 '16 at 22:24
  • If you want to have the possibility to safely change anything in the list, you have to make a deep clone of it. You can use Clone() method, but it can be tricky, because you must manually clone each object inside the list. – Al Kepp Aug 10 '16 at 22:29
  • @AlKepp - in this particular case it should be okay, since `Coordinate` only includes value types, so a shallow clone will suffice. – LeopardSkinPillBoxHat Aug 10 '16 at 22:39