0

For example i have the following class:

class Person
{
    public List<int> Grades{ get; set; }

    public Person(List<int> grades)
    {
        this.Grades = grades;
    }
}

And than i use this class in a main method , something like:

    static   void Main(string[] args)
    {
        List<int> grades = new List<int> { 1, 2, 3, 4, 5 };
        Person person = new Person(grades);

        for (int i = 0; i < 5; i++)
        {
            if (i % 2 == 0)
            {
                grades[i] = 0;
            }
            else
            {
                grades[i] = -1;
            }
        }

        foreach(var grade in person.Grades)
        {
            Console.Write(grade + " ");
        }

        Console.ReadKey();
    }

After running this code i expected to have on console the: 1 2 3 4 5 output result. Insteead i have this output result: 0 -1 0 -1 0

I expected that the collection "saved" into Person instance to stay how it was initialized. What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.

Can someone please explain me, why is this happening ?

Clock
  • 974
  • 3
  • 17
  • 35

4 Answers4

5

I expected that the collection "saved" into Person instance to stay how it was initialized.

It's time to revisit your expectations of how reference types work in C# then :) I have an article that you might find useful...

What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.

You can copy the collection in the constructor:

public Person(List<int> grades)
{
    this.Grades = new List<int>(grades);
}

Now for a List<int> that's fine - if you have a list of a mutable type, however, you potentially want to clone each element too. This sort of thing is why immutable types are nice - it's much easier to reason about their behaviour, because nothing can mess with instances, so you can just keep references...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2
 this.Grades = grades;

The above line only assign the reference held by grades to this.Grades so now both are pointing/referencing the same item in memory.

To resolve it, since it is a List<int>, you can do

 this.Grades = grades.ToList();

But if you have List<T> where T is a class or a reference type, you will still have the same problem. See: How create a new deep copy (clone) of a List<T>?

Community
  • 1
  • 1
Habib
  • 219,104
  • 29
  • 407
  • 436
2

Both the local variable grades and the field in the Person class are both referencing the same list. Evaluating either variable to its values result in the same list being returned, therefore mutating that one list that exists creates changes that are observable from either variable.

If you create a new list and copy all of the values from it to the new list when assigning the property's value in the constructor then you will have two separate lists, and mutating one won't be observable through the other.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

Lists are reference objects.

When you pass the list to the new person object, what you're really doing is passing it a pointer to the list, so the list in the person and the local list variable grades are actually the same list.

You can have the person object create a copy of the list instead using either of these approaches:

this.Grades = grades.ToList();
// or
this.Grades = new List<int>(grades);
Oblivious Sage
  • 3,326
  • 6
  • 37
  • 58