首页 > 解决方案 > 将元素添加到字典中的列表会产生意想不到的结果 c#

问题描述

我正在将后端转换为 c#,我注意到一个奇怪的行为

当我将元素添加到字典中的列表时

(实际上在另一个字典里面):

不久,它将值添加到字典中每个元素的每个列表中。

这是代码:

       public class Validator_Counter_Model
        {
            public readonly  Dictionary<string,Info_Model> Info;
            private Dictionary<string,Dictionary<DateTime,List<int>>> _map = new Dictionary<string, Dictionary<DateTime,List<int>>>();
            public Validator_Counter_Model(Dictionary<string, Info_Model> info)
            {
                Info = info;
            }
            public void Add(Validation_Element element)
            {

/// #### PROBLEM IS HERE!
                if (!_map.ContainsKey(element.Id))
                {
                    Dictionary<DateTime, List<int>> newmap = new Dictionary<DateTime, List<int>>();
                    _map.Add(element.Id, newmap);
                }

                DateTime fulldate = new Custom_Time_Model(element.Time).RegularTime;
                if (!_map[element.Id].ContainsKey(fulldate.Date))
                {
                    List<int> newList = new List<int>();
                    _map[element.Id].Add(fulldate.Date, newList);
                }
                _map[element.Id][fulldate.Date].Add(element.Time);
/// #### PROBLEM IS HERE!

            }

            public void Del(Validation_Element element)
            {
                DateTime fulldate = new Custom_Time_Model(element.Time).RegularTime;
                DateTime date = new DateTime(fulldate.Year, fulldate.Month, fulldate.Day);
                _map[element.Id][date].Remove(element.Time);
            }

            public void Update(Dictionary<string, Dictionary<DateTime, List<int>>> newMap) => _map = newMap;

            public Dictionary<string, Dictionary<DateTime, List<int>>> map => _map;

        }
    }

标签: c#listdictionarycollections

解决方案


Given that the dictionary keys are known types like string and DateTime, I can fill in implementations of the missing classes and the specifics don't really matter. I don't care if they contain meaningful values. I just want to see how everything behaves. Those are posted at the end:

Now it's possible to write a unit test to see if what you're describing is happening.

[TestMethod]
public void validation_elements_are_added_to_only_one_dictionary()
{
    var elementOne = new Validation_Element {Id = "A", Time = 1 };
    var elementTwo = new Validation_Element { Id = "B" , Time = 2};
    var elementThree = new Validation_Element { Id = "A" , Time = 3};

    var subject = new Validator_Counter_Model(new Dictionary<string, Info_Model>());
    subject.Add(elementOne);
    subject.Add(elementTwo);
    subject.Add(elementThree);

    var output = subject.map;

    var elementsWithId_A = output["A"];
    var elementsWithId_B = output["B"];

    var id_a_innerList = elementsWithId_A[new DateTime(2000, 1, 1)];
    var id_b_innerList = elementsWithId_B[new DateTime(2000, 1, 1)];

    Assert.AreEqual(2, id_a_innerList.Count);
    Assert.AreEqual(1, id_b_innerList.Count);
}

I'm adding two validation elements with Id "A" and one with Id "B". That means _map should contain two dictionaries. The key to the inner dictionary will always be 1/1/2000 so there will be two inner lists.

What I expect is that the inner list for A will have two items and the inner list for B will have one. If every item gets added to every list then they will both have two.

The test passes, which in this case was expected. Just from looking at the code we can see that Add is creating distinct lists and won't add an item to two lists. But now the test confirms that.

What that means is that if you are seeing items get added to multiple lists, the problem isn't where you thought it was. It's got to be somewhere else. That's one of the awesome things about unit tests. They give us a degree of certainty around one area of code so that if something isn't working we know that the problem is likely in the area that doesn't have unit tests. Narrowing down that search makes finding bugs faster and easier.

If we do find a problem with our unit test, then we can debug just that unit test instead of debugging the whole application. And then imagine if the other classes had unit tests, too. We're always going to make mistakes as we code, but with unit tests we just find them faster and fix them more easily.

In this case it's a little bit messy that I've made up fake implementations of your classes. Even though I don't think it makes a difference since they're just data, it does introduce a tiny degree of uncertainty. I'd replace them with the real ones and then adjust the test accordingly. And you can write more tests as needed.


Working implementations of the classes that weren't provided:

public class Validation_Element
{
    public string Id { get; set; }
    public int Time { get; set; }
}

public class Custom_Time_Model
{
    public Custom_Time_Model(int time)
    {
        Time = time;
        // This makes it possible to create classes where `Time` is different
        // but fullDate.Date (in the Add method) is the same. If I want that date
        // to be different I'll add 24 or more hours.
        RegularTime = new DateTime(2000,1,1).AddHours(time);
    }
    public DateTime RegularTime { get; set; }
    public int Time { get; set; }
}

public class Info_Model { }

推荐阅读