4

I basically have a list of Dictionaries like:

List<Dictionary<string, string>>

For the purposes of testing I'm fetching 36 dictionary items into the list and then returning the list at the end of my function.

The odd thing is when I populate the list, I can see the Key=>Value pairs of the dictionaries being added to the list in the Visual Studio Inspector, however upon clearing the original dictionary used to populate my list, all that remains is 36 empty items in the list.

Is there some weird List behaviour happening that I'm unaware of? A code snip is included below for reference...

    List<Dictionary<string, string>> allResults = new List<Dictionary<string, string>>();
    Dictionary<string, string> selectResult = new Dictionary<string, string>();

    MySqlCommand cmd = new MySqlCommand(query, conn);
    MySqlDataReader dataReader = cmd.ExecuteReader();

    try
    {
        while (dataReader.Read())
        {
            for (int i = 0; i < dataReader.FieldCount; i++)
            {
                selectResult.Add(dataReader.GetName(i).ToString(), dataReader.GetValue(i).ToString());
            }
            allResults.Add(selectResult);

            //Something to do with this next line seems to cause the List to also lose the values stored in the Dictionary, is clearing the dictionary not allowed at this point and the list is simply referencing the Dictionary rather than 'making a copy'?
            selectResult.Clear();
        }
        dataReader.Close();
    }

    catch { }

    this.Close();

    return allResults;
flexage
  • 457
  • 1
  • 9
  • 19

5 Answers5

5

You add the same INSTANCE of dictionary in the list for every loop.
It is only expected that when you clear the dictionary every one is emptied

to resolve the problem you need to add this to your cycle

   while (dataReader.Read())
   {
        // at every loop, create a new instance of dictionary using the same variable
        Dictionary<string,string> selectResult = new Dictionary<string, string>();
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            selectResult.Add(dataReader.GetName(i).ToString(), dataReader.GetValue(i).ToString());
        }
        // Adding a different copy of the dictionary in the list
        allResults.Add(selectResult);
    }

However I need to ask you. Why to use a dictionary to store columns and rows? You could achieve your result with a DataTable

    DataTable dt = new DataTable();
    dt.Load(dataReader);

and forget the List and the Dictionary

Steve
  • 213,761
  • 22
  • 232
  • 286
  • There really isn't any good reason to define the dictionary outside of the `while` loop at all. It only means you might forget to re-initialize it or use it before/after the scope of the loop, where it just doesn't make sense to be used. If you define it inside the loop you ensure it's initialized every time by the compiler, and ensure it can't be used in other contexts where it just doesn't make sense to be used. – Servy Mar 22 '13 at 16:00
  • Yes, I agree and fix the answer, however I am still perplexed by this reinvention of a datatable. – Steve Mar 22 '13 at 16:03
  • C# isn't my usual language to be coding in so some of the more intricate behaviours have evaded me until now, thank you all for your answers, they all explain very well where I was going wrong. Marking this one as correct as I used this answer and it has also given food for further thought as I haven't heard of datatable's before. – flexage Mar 22 '13 at 16:11
2

Because you did not clone it. And you copied the address of the first object. Use clone next time.

Deep cloning objects

Community
  • 1
  • 1
radu florescu
  • 4,315
  • 10
  • 60
  • 92
0

Dictionary is a reference type. You need to make a new one: selectResult = new Dictionary<string, string>() instead of calling Clear.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
0

You are adding a reference to the Dictionary to your list, so changes in the original dictionary are also reflected in the instances you access through the list. If you want to add a copy of the Dictionary, you'll need to use something like this:

allResults.Add(new Dictionary<string, string>(selectResult));
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
0

As others have said, when you add the dictionary to the list you are only adding an additional reference to the same existing dictionary. That said, I'd suggest a different solution than copying it.

The problem is that your dictionary is at the top level of scope. It shouldn't be. You're trying to re-use the same dictionary over and over. You'll be better off simply defining the dictionary at a lower level, inside of the while loop:

List<Dictionary<string, string>> allResults = new List<Dictionary<string, string>>();

MySqlCommand cmd = new MySqlCommand(query, conn);
MySqlDataReader dataReader = cmd.ExecuteReader();

try
{
    while (dataReader.Read())
    {
        Dictionary<string, string> selectResult = new Dictionary<string, string>();
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            selectResult.Add(dataReader.GetName(i).ToString(), dataReader.GetValue(i).ToString());
        }
        allResults.Add(selectResult);
    }
    dataReader.Close();
}
//...

Note the only change I made was moving the declaration of selectResult.

By creating a new dictionary for each iteration of the while loop you ensure that you're adding a new dictionary each time.

Servy
  • 202,030
  • 26
  • 332
  • 449