简体   繁体   English

C#字典的奇怪行为

[英]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. 当我第一次调用MakeList函数时,可以预期结果。

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. 当我重新声明变量ItemList ,我期望第二次调用该函数时看到相同的结果,但是就像先前调用的结果被缓存并重新使用一样。

Why this is happening and why is this behavior? 为什么会这样,为什么会这样? Is there any way to avoid it? 有什么办法可以避免呢?

You re-declaring ItemList , but using same objects from internal _Items : ItemClass _item = _Items[ItemID]; 您重新声明ItemList ,但使用内部_Items相同对象: ItemClass _item = _Items[ItemID]; and incrementing count on same object. 并增加对同一对象的计数。 It is why part :). 这就是为什么part :)。 avoid part is to create new items maybe. 避免部分可能是创建新项目。

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. 您刚刚在调用AddRecursive方法时修改了ItemClass对象中Count属性的值。

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对象或简单地创建新实例并复制属性的原始值即可。

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

Instead of this 代替这个

Itemclass _item = _Items[ItemId]

Your problem is that you alter the Count property of the ItemClass instances in the _Items dictionary while you run AddRecursive . 您的问题是您在运行AddRecursive _Items词典中ItemClass实例的Count属性。

Get rid of that mutable Count property (it really shouldn't be in ItemClass ), and simplify your code to 摆脱那个可变的Count属性(它实际上不应该在ItemClass ),并将代码简化为

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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM