0

I have this code:

List<List<string>> strs = new List<List<string>>();
List<string> str = new List<string>();

foreach (DataGridViewRow sr in dataGridView1.SelectedRows)
{
    if(i >= 10)
    {
        strs.Add(str); //Here strs have one str with 10 values
        str.Clear();
        i = 0;
        var a = strs; //Here strs have one str with 0 values
    }
    str.Add(sr.Cells["MOBILNI"].Value.ToString().Trim());
    i++;
}
strs.Add(str);

I have list of strings which i populate and when it reach 10 members i put whole list into list of List<string>, then clear list of strings to populate it with next 10 of items. Problem is that when list of strings reach 10 members, i add it to list of List<string> and in debugging i see my strs has 1 member (List<string>) which has 10 elements (10 strings) but when i clear my base list of strings it also clears that list inside List<List<string>>.

When i use str = new List<string>() instead of str.Clear() it works normally.

So why is this happening and how to overcome it?

Aleksa Ristic
  • 2,394
  • 3
  • 23
  • 54
  • When you do this: `var a = strs;` You're declaring a variable that immediately goes out of scope. So this line isn't doing anything. It's declared within the `if` block, so it doesn't exist outside of that block. – Scott Hannen May 21 '18 at 13:09
  • What exactly are you trying to achieve? What is the point of putting your 10-element list into another list? What's happening is that your 10-element list only exists once. When you put it into the other list, you're just putting a reference to it. – rory.ap May 21 '18 at 13:10
  • It is not doing anything, it was just for testing purpose so i can see values of `strs` in debugger – Aleksa Ristic May 21 '18 at 13:10

3 Answers3

5

When you write strs.Add(str) you add the exact same instance if List<string> to your list of lists, that you clear afterwards. Calling str.Clear will thus be reflected in all references to that instance, also in strs[0], strs[1] and so on. This happens because a List<T> is a reference-type. Doing anything with one reference will be reflected in all references. Value-types on the other hand will copy the entire object whenever you pass it to a method. Thus the following code will resulst in i having the value 4 after calling DoSomething.

int i = 4;
var result = DoSomething(i);

void DoSomething(int j)
{
    j = -7;
}

To circumvent this, you´d have to create a completely new list, instead of just adding the existing one:

foreach (DataGridViewRow sr in dataGridView1.SelectedRows)
{
    if(i >= 10)
    {
        strs.Add(new List<string>(str));
        str.Clear();
        i = 0;
        var a = strs; //Here strs have one str with 0 values
    }
    str.Add(sr.Cells["MOBILNI"].Value.ToString().Trim());
    i++;
}
strs.Add(str);
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • If this (great) answer confuses you, make sure you understand that Lists are reference types - and what that implies. – Mihai Ovidiu Drăgoi May 21 '18 at 13:13
  • Yea thank you @MihaiOvidiuDrăgoi that is what i needed. Answer explains something but now you mentioned it i have full picture of it. I will accept this answer in 4 min. – Aleksa Ristic May 21 '18 at 13:16
1

You need to add a copy of the original list before clearing, so you can use .ToList

foreach (DataGridViewRow sr in dataGridView1.SelectedRows)
{
    if(i >= 10)
    {
        strs.Add(str.ToList()); //Here strs have one str with 10 values
        str.Clear();
        i = 0;
        var a = strs; //Here strs have one str with 0 values
    }
    str.Add(sr.Cells["MOBILNI"].Value.ToString().Trim());
    i++;
}
strs.Add(str);

Your other option is, instead of clearing it, set str to a brand new list:

foreach (DataGridViewRow sr in dataGridView1.SelectedRows)
{
    if(i >= 10)
    {
        strs.Add(str); //Here strs have one str with 10 values
        str = new List<string>();
        i = 0;
        var a = strs; //Here strs have one str with 0 values
    }
    str.Add(sr.Cells["MOBILNI"].Value.ToString().Trim());
    i++;
}
strs.Add(str);
Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
0

When you are adding the List<string> to List<List<string>> you are actually just passing the pointer (reference) of List<string>, not the whole object. Thats why when you call str.clear(), it clears the list object, to which the pointer in List<List<string>> is pointing to. Instead of calling clear() you should create a new object (str = new List<string>();) or clone() the object when adding it to the List<List<string>>.

AndrejH
  • 2,028
  • 1
  • 11
  • 23