1

So I try to create a fake list of item

var items = new List<Item>();

var item = new Item 
{
    Name = "A";
} 

for(int i=0; i<3; i++)
{
    items.Add(item);
}

item.Name = "B";

for(int i = 0; i<2; i++)
{
    items.Add(item);
}

I expect it would return a list with 3 items Name A, and 2 items Name B, but it return 5 item Name B. Where am I wrong?

KaMaHe
  • 413
  • 1
  • 7
  • 18
  • 1
    You are refering to the same item everytime – Beingnin Feb 07 '20 at 09:35
  • 1
    You don't create a new `Item` you set the `Name` of the single `Item` you created to `"B"`. All you add to the list are references to that single `Item`. –  Feb 07 '20 at 09:35
  • 2
    Problem is, that when adding an `Item` to the list, it's not being copied. Rather, a reference to the `Item` is added to the list, meaning, your variable `item` and your list entry point to the same object, meaning if you change something on one, the other gets changed as well. What you want are separate items, so you'd need to create a new `Item` every loop – MindSwipe Feb 07 '20 at 09:35
  • You should do `item = new Item(); item.Name = "B";` – Chetan Feb 07 '20 at 09:35
  • @ChetanRanpariya that's not the problem, plus, the object initialization syntax is preferred to assignment, Visual Studio itself even shows you a tip (IDE0017) to use object initialization. Plus this: https://stackoverflow.com/a/12842511/9363973 – MindSwipe Feb 07 '20 at 09:41
  • @MindSwipe I am not sure why my suggested code won't solve the problem.... and as it is obvious that `object initialization syntax` is preferred, it is not must.... https://dotnetfiddle.net/KgkpVL – Chetan Feb 07 '20 at 09:52
  • @ChetanRanpariya after doing what you commented, every "A" object would still point to the same "A" object, and not to 3 different "A" objects ([proof](https://dotnetfiddle.net/c7dEFE)). And yes, object initialization syntax is not a must, but as you said it is preferred, and teaching new programmers the preferred way of doing things is... well, preferred – MindSwipe Feb 07 '20 at 09:58
  • Does this answer your question? [Adding to list repopulates with the last element](https://stackoverflow.com/questions/6232118/adding-to-list-repopulates-with-the-last-element) and bazillion others – Selvin Feb 07 '20 at 10:08
  • @MindSwipe You are right about the same object but that's not the issue faced by or mentioned by the OP.... My solution gives 3A and 2B as expected... I am not saying that my solution is perfect but it's not wrong.... – Chetan Feb 07 '20 at 10:12

5 Answers5

3

The problem in your code is

  1. You created an item object with Name property as "A"
  2. you inserted the same object into three spaces of the collection but all pointing to the same object
  3. You changed the Name property to "B".
  4. You inserted the same object into two new spaces of the collection
  5. Since the variable item and all the 5 spaces of the collection points to the same object, any change going to any of these references will change the object and reflects in all references

For more details google reference typed variables

var items = new List<Item>();



for(int i=0; i<3; i++)
{
    var item = new Item() 
    {
        Name = "A";
    } 
    items.Add(item);
}



for(int i = 0; i<2; i++)
{     
    var item = new Item() 
    {
        Name = "B";
    } 
    items.Add(item);
}
Beingnin
  • 2,288
  • 1
  • 21
  • 37
  • You can remove the `()` here – aloisdg Feb 07 '20 at 09:38
  • @aloisdgmovingtocodidact.com Yeah it isn't necessary – Beingnin Feb 07 '20 at 09:41
  • 1
    Please be aware that as you are declaring the variable within the loop, each declaration results in a new object. Hence all of the `Item` values within the `List` are not related as they were in the original code. – Kami Feb 07 '20 at 09:47
  • @Kami - but usually that *would* be the preferred outcome – Hans Kesting Feb 07 '20 at 09:47
  • @HansKesting given that the OP is confused about reference types, it might be worth making this point clear. – Kami Feb 07 '20 at 09:51
  • 1
    I'll play devils advocate for a second here, but technically point 1 and point 2 should be consolidated to the same point. Because OP is using the object initialization syntax only returns the object once it has finished initializing every property. So `var item = new Item();` (with an empty constructor) wouldn't initialize the Name string yet, that would only happen after `item.Name = "A";` finished. So technically the object isn't being initialized and then have the name changed, but the name would be initialized in the same step. Source: https://stackoverflow.com/a/12842511/9363973 – MindSwipe Feb 07 '20 at 10:04
  • @MindSwipe Yeah i lean with your point. I will update the answer as u suggested – Beingnin Feb 07 '20 at 10:07
2

In C# and most modern languages, objects are treated as reference types. This means that there is only one copy of the actual object in memory and each instance of that variable is just a reference pointing to this copy.

As a result when a reference of the object has been modified all instances where it is referenced get the new value. The code

var item = new Item() 
{
    Name = "A";
} 

and

item.Name = "B";

are modifying the same object. Hence, all the values in the list have the name A, which you then change to B and assign 2 more copies of this to the array - resulting in 5 total.

If you are looking to have 3 of A and 2 of B then you need to declare a new object and assign this. Try the following:

var items = new List<Item>();

var itemA = new Item() 
{
    Name = "A";
} 

for(int i=0; i<3; i++)
{
    items.Add(itemA);
}
// item list now has 3 A
var itemB = new Item() 
{
    Name = "B";
} 

for(int i = 0; i<2; i++)
{
    items.Add(itemB);
}
// itme list now has 3 A and 2 B

Further reading:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/objects and to contrast with reference types - structs or value types - https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/structs

Kami
  • 19,134
  • 4
  • 51
  • 63
  • Great answer, but, not creating a new object every iteration would mean that the list doesn't contain 3 items with the Name "A", but 3 references to the same item with the Name "A" – MindSwipe Feb 07 '20 at 09:54
2

The problem here is that you are adding references to you created object (item) to the list, not different objects. So the 5 items in your list, and your item variable are all the same object, meaning, if you change one, you change every other reference to it as well.

Try this for clarity:

var item = new Item
{
    Name "Hello "
};

var secondItem = item;
item.Name = "World";

// This will print 'WorldWorld'
Console.WriteLine(item.Name + secondItem.Name);

Sane principle is happening to your list items, just that instead of the separate variable secondItem your second (and third, and fourth, ...) item isn't in a variable, but being held by the list.

Specifically to solve your problem, you'll need to create a new object on every iteration of your loops. Like so:

var items = new List<Item>();
Item item;

for (int i = 0; i < 3; i++)
{
    item = new Item
    {
        Name = "A"
    };

    items.Add(item);
}

for (in i = 0; i < 2; i++)
{
    item = new Item
    {
        Name = "B";
    };

    items.Add(item);
}

This code can be reduced to a few lines (6 to be specific), but I left it this long as it is easier to read and more importantly easier to understand like this

MindSwipe
  • 7,193
  • 24
  • 47
1
var item = new Item() 
{
    Name = "A";
}  

Creates an Object. An Object has reference semantics, i.e. if you change the name, all references will see the same name. You have to add new objects to a list, e.g. create a new item in your loops.

Christian Sauer
  • 10,351
  • 10
  • 53
  • 85
1

In the list are stored references to the variable item. Changing item.Name changes name of the so when you look items in the list, all will have name set to "B", because there are references to the item object, which's name is now "B". If you want to have only the last two items name "B", instead of

item.Name = "B";

use this:

var item = new Item{
    Name = "B"
};
Encyklopedie
  • 111
  • 1
  • 6