Добавление элемента в список и словарь дает неожиданные результаты c #

Я конвертирую back-end в 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;

        }
    }

Всего 2 ответа


Учитывая, что ключи словаря являются известными типами, такими как string и DateTime , я могу заполнить реализации отсутствующих классов, и специфика на самом деле не имеет значения. Мне все равно, если они содержат значимые значения. Я просто хочу посмотреть, как все себя ведет. Те размещены в конце:

Теперь можно написать модульный тест, чтобы увидеть, происходит ли то, что вы описываете.

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

Я добавляю два элемента проверки с идентификатором «A» и один с идентификатором «B». Это означает, что _map должен содержать два словаря. Ключ к внутреннему словарю всегда будет 1/1/2000 поэтому будет два внутренних списка.

Я ожидаю, что внутренний список для A будет содержать два элемента, а внутренний список для B - один. Если каждый элемент будет добавлен в каждый список, то у них будет по два.

Испытание проходит, что в этом случае было ожидаемо. Просто взглянув на код, мы видим, что Add создает отдельные списки и не добавляет элемент в два списка. Но теперь тест подтверждает это.

Это означает, что если вы видите, что элементы добавляются в несколько списков, проблема не в том, где вы думали. Это должно быть где-то еще. Это одна из замечательных вещей в модульных тестах. Они дают нам определенную степень уверенности в отношении одной области кода, так что если что-то не работает, мы знаем, что проблема, скорее всего, в области, где нет модульных тестов. Сужение этого поиска делает поиск ошибок быстрее и проще.

Если мы обнаружим проблему с нашим модульным тестом, то мы можем отладить только этот модульный тест вместо отладки всего приложения. А потом представьте, если бы у других классов тоже были юнит-тесты. Мы всегда будем делать ошибки при кодировании, но с помощью модульных тестов мы просто находим их быстрее и исправляем их легче.

В этом случае немного запутанно, что я выдумал фальшивые реализации ваших классов. Несмотря на то, что я не думаю, что это имеет значение, поскольку они являются просто данными, это вносит незначительную степень неопределенности. Я бы заменил их настоящими, а затем соответствующим образом скорректировал тест. И вы можете написать больше тестов по мере необходимости.


Рабочие реализации классов, которые не были предоставлены:

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

по сути, второй словарь (тот, который находится в значении первого) - это не сам словарь, а ссылка на другой словарь, поэтому добавление значения в его список изменяет весь список для каждого ключа


Есть идеи?

10000