Member 12296654 Ответов: 4

Как создать резервную копию словаря на языке Си#


Привет,

У меня есть словарь со словарем<int,string> dict_1 и еще один в качестве резервной копии с тем же типом dict_2. Любые изменения в dict_1 не должны отражаться в dict_2. когда пользователь хочет вернуть исходный словарь, dict_2 должен быть скопирован в dict_1. Пожалуйста, помогите мне в этом.

Что я уже пробовал:

Попробовал следующий код, но не смог работать,
Dictionary<int,string> dict_1 =new Dictionary<int, string="">();
dict_1.Add(1,"a");
dict_1.Add(1,"b");
dict_1.Add(1,"c");
Dictionary<int, string=""> dict_2 =new Dictionary<int,string>(dict_1);
dict_1.Remove(1);//affects dict_2 also which should not happen.

Richard Deeming

Ваш пример кода не будет работать, так как вы добавили тот же ключ к dict_1 три раза.

Исправляя это, ваш пример кода ведет себя не так, как вы описали. Удаление элемента из dict_1 не удаляет его из dict_2.

Обновите свой вопрос, чтобы показать фактический код, который вы используете.

phil.o

Предоставленный вами код даже не будет компилироваться из-за string="" в dict_2 но я предполагаю, что это просто опечатка, когда вы написали свой вопрос.
Но есть еще одна серьезная проблема: вы пытаетесь добавить в словарь запись, ключ которой уже существует; третья строка кода, которую вы показываете, выдаст ошибку. ArgumentException потому что вы пытаетесь добавить существующий ключ в словарь.
Наконец, использование конструктора, как вы это сделали, не должно приводить к равенству экземпляров между dist_1 и dist_2. В соответствии с Словарь<TKey,TValue>(IDictionary<TKey,TValue>)[^], элементы из dict_1 будет скопировано в dict_2.
Все это заставляет меня думать, что код, который вы показали, не совсем тот код, который у вас есть. Пожалуйста, убедитесь, что у нас есть ваш фактический код.

4 Ответов

Рейтинг:
1

lmoelleb

Первый шаг-исправить код, чтобы он компилировался.

Второй шаг-исправить код, чтобы он работал.

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

Например, вы получите что-то вроде этого (используя консоль.WriteLine, конечно, вы бы использовали отладчик)

using System;
using System.Collections.Generic;
					
public class Program
{
    public static void Main()
    {
        Dictionary<int,string> dict_1 =new Dictionary<int, string>();
        dict_1.Add(1,"a");
        dict_1.Add(2,"b");
        dict_1.Add(3,"c");
        Dictionary<int, string> dict_2 =new Dictionary<int,string>(dict_1);
        dict_1.Remove(1);//affects dict_2 also which should not happen. 
        Console.WriteLine(dict_1.Count);
        Console.WriteLine(dict_2.Count);
    }
}


Вы можете запустить его здесь:
Онлайн-компилятор C# | .NET Fiddle[^]

Как и ожидалось, выходы 2 следуют за 3, поэтому явно содержание словарей отличается. Отладчик позволит вам наблюдать точное содержание словаря.


Maciej Los

5ed!

Рейтинг:
1

Graeme_Grant

Вот решение, которое позволит:
1. Создайте словарь и загрузите данные
2. резервное копирование (сериализация) словаря в строку, так как нам не нужно обращаться к нему
3. Удалите элемент
4. восстановление исходного словаря

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("-- start --");

        var dict1 = new Dictionary<int, string>
        {
            [0] = "ABC",
            [1] = "DEF",
            [2] = "GHI",
        };

        foreach (var item in dict1)
            Console.WriteLine($"{item.Key} = {item.Value}");

        Console.WriteLine("-- serialize --");

        var bkup = JsonConvert.SerializeObject(dict1);

        Console.WriteLine(bkup);


        Console.WriteLine("--remove item --");

        dict1.Remove(1);

        foreach (var item in dict1)
            Console.WriteLine($"{item.Key} = {item.Value}");

        Console.WriteLine("--restore --");
        dict1 = JsonConvert.DeserializeObject<Dictionary<int, string>>(bkup);

        foreach (var item in dict1)
            Console.WriteLine($"{item.Key} = {item.Value}");

        Console.ReadKey();
    }
}

И выход есть:
-- start --
0 = ABC
1 = DEF
2 = GHI
-- serialize --
{"0":"ABC","1":"DEF","2":"GHI"}
--remove item --
0 = ABC
2 = GHI
--restore --
0 = ABC
1 = DEF
2 = GHI

Для этого примера я использовал библиотеку NewtonSoft JSON. Вы можете узнать больше об этой библиотеке здесь: [^]


Рейтинг:
0

BillWoodruff

Недавно я опубликовал пример слияния двух словарей, где в случае повторяющихся значений вы можете контролировать, какое значение используется в объединенном (новом) словаре [^]

Для того чтобы диктант2 был по-настоящему независимый из диктанта 1 вы должны сделать глубокий однако копирование пар KeyValuePairs, если ключи являются целыми числами, а значения строками, это легко, потому что задействованные типы не требуют реализации ICloneable.

Вы можете просто сделать это:

Dictionary<int, string> dict1 = new Dictionary<int, string>(dict2);
Вы ошибаетесь, когда утверждаете, что удаление KeyValuePair из dict1 повлияет на dict2.


lmoelleb

Будьте осторожны с ICloneable - он не указывает, является ли клон глубоким или мелким.

BillWoodruff

Поскольку я бы реализовал ICloneable только тогда, когда типы, которые я использовал, требовали глубокой копии, я не совсем понимаю, что вы здесь делаете. Пожалуйста, скажи больше.

lmoelleb

Реализация его в виде глубокой копии-это ваше предпочтение. Это не то, что предписано интерфейсом или его документацией (наоборот). Это означает, что каждый раз, когда вы видите интерфейс, вы должны думать: "это один из моих глубоких клонов, или я ссылаюсь на что-то написанное другим парнем, который мог бы сделать что-то еще". Для меня интерфейс просто не стоит использовать, когда мне приходится иметь дело с потенциальными будущими проблемами.

Maciej Los

5ed!

BillWoodruff

Реализация ICloneable не является предпочтением: это требование, когда вы хотите глубоко скопировать сложные, изменяемые объекты.

Это не единственный инструмент: вы можете использовать двоичную сериализацию. Я предлагаю вам изучить, что такое ICloneable и что это значит, а не фантазировать о воображаемых проблемах. В то время как MS предлагает ICloneable не использоваться в публичных API, это не имеет ничего общего с этим потоком.

https://docs.microsoft.com/en-us/dotnet/api/system.icloneable-что?redirectedfrom=MSDN&view=netframework-4.8

lmoelleb

Quoting from the very link you are providing: "It does not specify whether the cloning operation performs a deep copy, a shallow copy, or something in between". There is no built in interface that guaranties deep clones in .NET. Sure you can use ICloneable for it, just be aware that simply because an object implements ICloneable, you can't guarantie it is a deep copy without further information (you wrote it yourself, it is documented, you read the code, …). You call that an imaginary problem. I call it something to be aware of so it does not become a real problem. I have done my share of cloning, including everything from cloning in the constructor over ICloneable, custom IDeepCloneable etc all the way to emitted code with reflection. Can you please point me in the direction of what additional information I can study on this subject?

BillWoodruff

Боюсь, что мы находимся здесь на двух разных частотах, и я напрасно трачу время, пытаясь прояснить это :) То, что вы говорите, кажется мне одновременно очевидным и совершенно неуместным, учитывая, о чем идет речь в этой теме.

Реализация интерфейсов гарантирует только наличие структур с определенными сигнатурами: конечно, с ними можно сделать кровавую кашу.

Рейтинг:
0

George Swan

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


 var missingKeys = dictA.Where(p => !dictB.ContainsKey(p.Key));
 var missingValues = dictA.Where(p => dictB.ContainsKey(p.Key) && dictB[p.Key]!=p.Value);
foreach(var p in missingKeys)
 {
     dictB.Add(p.Key, p.Value);
 }
 foreach (var p in missingValues)
 {
     dictB[p.Key]=p.Value;
 }