AaA
AaA

Reputation: 3694

Strange behavior of C# Dictionary

I have following simplified program

using System;
using System.Collections.Generic;
using System.Text;

class ItemClass {
    public int Id = 0;
    public int[] Childs = new int[] { };
    public int Count = 0;
}

class Class1 {

    Dictionary<int, ItemClass> _Items = new Dictionary<int, ItemClass> { };

    private void AddRecursive(int ItemID, int count, ref Dictionary<int, ItemClass> ItemList) {

        if (!_Items.ContainsKey(ItemID)) {
            return;
        }
        ItemClass _item = _Items[ItemID];
        if (!ItemList.ContainsKey(ItemID)) {
            ItemList.Add(ItemID, _item);
        }
        ItemList[ItemID].Count += count;
        if (_item.Childs.Length > 0) {
            foreach (int tmpItem in _item.Childs) {
                AddRecursive(tmpItem, ItemList[ItemID].Count, ref ItemList);
            }
        }
    }

    public void Add(int item, int[] childs) {
        if (!_Items.ContainsKey(item)) {
            _Items.Add(item, new ItemClass() { Id = item, Childs = childs, Count = 0 });
        }
    }

    public Dictionary<int, ItemClass> MakeList(int ItemID) {
        if (!_Items.ContainsKey(ItemID)) {
            return new Dictionary<int, ItemClass> { };
        }
        Dictionary<int, ItemClass> ItemList = new Dictionary<int, ItemClass> { };
        AddRecursive(ItemID, 1, ref ItemList);
        return ItemList;
    }

}


class Program {

    static void Main(string[] args) {
        Class1 class1 = new Class1();
        class1.Add(1111, new int[] { });
        class1.Add(2222, new int[] { 1111 });
        class1.Add(3333, new int[] { 1111, 2222 });

        Dictionary<int, ItemClass> items1 = class1.MakeList(3333);
        foreach (ItemClass item in items1.Values) {
            Console.WriteLine(item.Id + "  " + item.Count);
        }

        Console.WriteLine("");

        Dictionary<int, ItemClass> items2 = class1.MakeList(3333);
        foreach (ItemClass item in items2.Values) {
            Console.WriteLine(item.Id + "  " + item.Count);
        }

        Console.ReadKey();
    }
}

It has a simple task of counting items and showing a list of items and their count. When I call the MakeList function the first time, results are expected.

Expected:

3333  1
1111  2
2222  1

3333  1
1111  2
2222  1

Actual

3333  1
1111  2
2222  1

3333  2
1111  7
2222  3

As I am re-declaring the variable ItemList, I'm expecting to see the same result when I call the function for second time, however it is like the results from previous call is cached and re-used.

Why this is happening and why is this behavior? Is there any way to avoid it?

Upvotes: 1

Views: 119

Answers (3)

Nugi
Nugi

Reputation: 872

Actually there is nothing strange with it. You've just modified the value of Count property in your ItemClass object when you call AddRecursive method.

Assuming that you really want to get what you expected, you just need to deep clone your ItemClass object or simply create new instance and copy the original value of property.

ItemClass _item = new ItemClass() { Childs = _Items[ItemID].Childs, Count = _Items[ItemID].Count, Id = _Items[ItemID].Id };

Instead of this

Itemclass _item = _Items[ItemId]

Upvotes: 1

sloth
sloth

Reputation: 101162

Your problem is that you alter the Count property of the ItemClass instances in the _Items dictionary while you run AddRecursive.

Get rid of that mutable Count property (it really shouldn't be in ItemClass), and simplify your code to

class ItemClass 
{
    public int Id = 0;
    public int[] Childs = new int[] { };
}

class Class1 
{

    Dictionary<int, ItemClass> _Items = new Dictionary<int, ItemClass>();

    private void AddRecursive(int itemId, Dictionary<int, int> itemList) 
    {

        if (!_Items.ContainsKey(itemId)) 
            return;

        if (!itemList.ContainsKey(itemId)) 
            itemList.Add(itemId, 1);
        else
            itemList[itemId] += 1;

        var item = _Items[itemId];
        if (item.Childs.Length > 0) 
            foreach (int tmpItem in item.Childs)
                AddRecursive(tmpItem, itemList);
    }

    public void Add(int item, int[] childs) 
    {
        if (!_Items.ContainsKey(item)) 
            _Items.Add(item, new ItemClass() { Id = item, Childs = childs });
    }

    public Dictionary<int, int> MakeList(int itemId)
    {
        if (!_Items.ContainsKey(itemId)) 
            return new Dictionary<int, int>();

        var itemList = new Dictionary<int, int>();
        AddRecursive(itemId, itemList);
        return itemList;
    }
}

Upvotes: 0

EekTheCat
EekTheCat

Reputation: 127

You re-declaring ItemList, but using same objects from internal _Items : ItemClass _item = _Items[ItemID]; and incrementing count on same object. It is why part :). avoid part is to create new items maybe.

Upvotes: 1

Related Questions