Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.72/25: Рейтинг темы: голосов - 25, средняя оценка - 4.72
140 / 133 / 88
Регистрация: 18.05.2013
Сообщений: 399

Создание полной копии (deep copy) объекта

07.02.2016, 12:15. Показов 5440. Ответов 13
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет,

понадобилось в проекте создать полную копию объекта, для чего воспользовался двоичной сериализацией:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public static class ExtensionMethods
    {
        public static T DeepClone<T>(this T obj)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;
 
                return (T)formatter.Deserialize(ms);
            }
        }
    }
Все хорошо, но почему-то после этого ссылки на объект потерялись. Чтобы было понятнее, подготовил простой тестовый проект, где можно воспроизвести проблему.

C#
1
2
3
4
5
6
    [Serializable]
    public class DateAndTime
    {
        public bool Date { get; set; }
        public bool Time { get; set; }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    public partial class MainForm : Form
    {
        private DateAndTime obj;
 
        public MainForm()
        {
            InitializeComponent();
 
            obj = new DateAndTime();
            obj.Date = true;
            obj.Time = false;
        }
 
        private void btnOpen_Click(object sender, EventArgs e)
        {
            DateAndTime newObj = obj;
            ChildForm childForm = new ChildForm(newObj);
 
            childForm.ShowDialog(this);
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public partial class ChildForm : Form
    {
        private DateAndTime parentObj = null; //parent object
        private DateAndTime newObj = null; //deep copy
 
        public bool Date
        {
            get { return chbDate.Checked; }
            set { chbDate.Checked = value; }
        }
 
        public bool Time
        {
            get { return chbTime.Checked; }
            set { chbTime.Checked = value; }
        }
 
        public ChildForm(DateAndTime obj)
        {
            InitializeComponent();
 
            parentObj = obj; //references are equal
            newObj = obj.DeepClone(); //deep copy made by binary serialization
            
            Date = newObj.Date;
            Time = newObj.Time;
        }
 
        private void btnSave_Click(object sender, EventArgs e)
        {
            parentObj = newObj;
 
            parentObj.Date = newObj.Date;
            parentObj.Time = newObj.Time;
        }
    }
Имеется две формы, при нажатии на кнопку Open на первой форме открывается вторая форма и в конструктор класса передается объект класса DateAndTime. После изменения настроек и их сохранения все изменения должны быть отражены и в объекте, который передавался в конструктор класса, но этого не происходит.

Подскажите, пожалуйста, почему так происходит и что я сделал не так? Если не создавать полной копии и просто скопировать все поля класса DateAndTime в конструкторе, то все работает, как и ожидалось.
Вложения
Тип файла: rar DeepCopyTest.rar (64.5 Кб, 6 просмотров)
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
07.02.2016, 12:15
Ответы с готовыми решениями:

Создание полной копии объекта с рефлексией
Не знаю, будет ли кому-то интересно, все описал тут: https://www.cyberforum.ru/blogs/188200/blog1518.html

Создание копии объекта через сериализацию
Всем доброго времени суток! Реализую так: public static T CopyFromSerialize&lt;T&gt;(T SerializableObject) where T : new() { try ...

Добавление копии объекта в массив
Предполагаю, что решение очень простое. Как сделать так, чтобы в список были не клоны объекта, а полноценные копии? class Program ...

13
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
07.02.2016, 12:41
Лучший ответ Сообщение было отмечено atoi как решение

Решение

Цитата Сообщение от atoi Посмотреть сообщение
почему так происходит и что я сделал не так?
Так происходит потому, что в конструктор второй формы вы передаете копию ссылки на объект.
Во второй форме вы объект клонируете и присваиваете полю ссылку на уже другой объект. Но это никак не меняет переданную в конструктор переменную, которая по-прежнему ссылается на старый, не клонированный объект.

Вам надо ссылку на новый объект во второй форме сделать открытой и в первой форме отслеживать закрытие второй, после чего самостоятельно доставать оттуда ссылку на новый объект и присваивать ее переменной.
Ну или создайте класс-обертку, который будет содержать ссылку на DateAndTime и передавайте во вторую форму ссылку на экземпляр этой обертки. В ней же и меняйте значение переменной на ссылку на клонированный объект.
1
140 / 133 / 88
Регистрация: 18.05.2013
Сообщений: 399
07.02.2016, 13:15  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Так происходит потому, что в конструктор второй формы вы передаете копию ссылки на объект.
Во второй форме вы объект клонируете и присваиваете полю ссылку на уже другой объект. Но это никак не меняет переданную в конструктор переменную, которая по-прежнему ссылается на старый, не клонированный объект.
Спасибо, но я все равно не понял до конца, не могли бы вы объяснить? В конструктор второй формы передается объект класса DateAndTime, newObj содержит ссылку на obj:
C#
1
DateAndTime newObj = obj;
В конструкторе второй формы объект newObject содержит полную копию объекта, переданного в конструктор, а parentObj содержит ссылку на объект, переданный в конструктор формы. При нажатии на кнопку Save происходит копирование ссылок:
C#
1
parentObj = newObj;
в результате чего parentObj, newObj и obj, переданный в конструктор, должны быть одинаковы, т.к. имеют одинаковые ссылки.

Добавлено через 16 минут
Кстати, в обрабтчике события btnSave код должен быть такой:

C#
1
2
3
4
5
6
7
private void btnSave_Click(object sender, EventArgs e)
        {
            parentObj = newObj;
 
            parentObj.Date = Date;
            parentObj.Time = Time;
        }
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
07.02.2016, 13:37
Лучший ответ Сообщение было отмечено atoi как решение

Решение

Цитата Сообщение от atoi Посмотреть сообщение
в результате чего parentObj, newObj и obj, переданный в конструктор, должны быть одинаковы, т.к. имеют одинаковые ссылки.
При работе с ссылочными типами вы оперируете переменными-ссылками. Как при передаче в метод переменной ссылочного типа, так и значимого типа передается копия переменной. =>
После этой строки: parentObj = newObj; на новый объект будет указывать переданная копия ссылки, а не исходная.
Можно сделать вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public ChildForm(ref DateAndTime obj)
        {
            InitializeComponent();
 
            parentObj = obj; //references are equal
            newObj = obj.DeepClone(); //deep copy made by binary serialization
            
            Date = newObj.Date;
            Time = newObj.Time;
        }
... 
//Ну и вызов
private void btnOpen_Click(object sender, EventArgs e)
        {
            DateAndTime newObj = obj;
            ChildForm childForm = new ChildForm(ref newObj);
 
            childForm.ShowDialog(this);
            obj = newObj; // теперь эти переменные ссылаются на новый - deep copy - oбъект
        }
1
140 / 133 / 88
Регистрация: 18.05.2013
Сообщений: 399
07.02.2016, 13:48  [ТС]
IamRain, спасибо большое за доходчивое объяснение! Я, по своей глупости, думал, что при передаче переменных ссылочного типа передается ссылка, а не копия, на что указал уважаемый kolorotur.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
07.02.2016, 13:51
Цитата Сообщение от atoi Посмотреть сообщение
создать полную копию объекта
atoi, для кого был придуман интерфейс IClonable()?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
07.02.2016, 13:58
Цитата Сообщение от atoi Посмотреть сообщение
В конструктор второй формы передается объект класса DateAndTime, newObj содержит ссылку на obj
Передается не объект, а переменная, содержащая ссылку на объект, причем передается копия переменной.

Цитата Сообщение от atoi Посмотреть сообщение
В конструкторе второй формы объект newObject содержит полную копию объекта, переданного в конструктор, а parentObj содержит ссылку на объект, переданный в конструктор формы. При нажатии на кнопку Save происходит копирование ссылок, в результате чего parentObj, newObj и obj, переданный в конструктор, должны быть одинаковы, т.к. имеют одинаковые ссылки.
Они одинаковы, да, но друг с другом никак не связаны. Если вы поменяете значение одной переменной, значения других переменных не поменяются.

В качестве аналогии можете взять файловую систему: есть файл, содержащий данные — это объект. Есть ярлык на файл — это переменная, а значение переменной — путь к файлу.
Вы берете ярлык, лежащий на рабочем столе (в первой форме), копируете его и вставляете в другую папку (передаете в конструктор второй формы). Теперь у вас два ярлыка, ссылающиеся на один и тот же файл.
В другой папке вы меняете путь скопированного ярлыка на другой файл. Поменяется ли от этого путь к файлу в ярлыке, лежащем на рабочем столе? Нет, потому что вы изменяете копию ярлыка (копию переменной). В результате у вас два ярлыка (две переменные), ссылающиеся на два разных файла (два объекта).
1
140 / 133 / 88
Регистрация: 18.05.2013
Сообщений: 399
07.02.2016, 14:28  [ТС]
Цитата Сообщение от insite2012 Посмотреть сообщение
atoi, для кого был придуман интерфейс IClonable()?
Хотя использование интерфейса IClonable() лучше по скорости, но он имеет существенный недостаток: при добавлении новых полей в класс нужно не забыть об обновлении метода Clone().

Добавлено через 18 минут
IamRain, проверил ваш пример, он не работает, похоже, потому что во второй форме определена переменная parentObj, содержащая ссылку на переменную, переданную в конструктор второй формы
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
07.02.2016, 16:03
хм, странно, не могу понять почему после возрата из диалога Debug.WriteLine в строке 17 выдает true - это ведь должны быть ссылки уже на разные объекты. Может кто объяснить?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
            private void btnSave_Click(object sender, EventArgs e)
        {
            Debug.WriteLine("1)Refs are equal: {0}", Object.ReferenceEquals(newObj, parentObj));//false - deep copy имеет другой адрес
            parentObj = newObj;
            Debug.WriteLine("2)Refs are equal: {0}", Object.ReferenceEquals(newObj, parentObj));//соответственно true после присваивания
            int k = 0;
            parentObj.Date = newObj.Date;
            parentObj.Time = newObj.Time;
        }
...
//родительская форма
private void btnOpen_Click(object sender, EventArgs e)
        {   
            DateAndTime newObj = obj;
            ChildForm childForm = new ChildForm(ref newObj);
            childForm.ShowDialog(this);
            Debug.WriteLine("3) Refs are equal: {0}", Object.ReferenceEquals(newObj, obj)); // true - не могу понять              почему
            int k = 0;
        }
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
07.02.2016, 16:18
Цитата Сообщение от IamRain Посмотреть сообщение
не могу понять
Я вот вообще не могу понять все эти танцы с бубном, зачем они и к чему... Сериализация, то, се...
Нужен один объект по всей программе? Так создать класс-синглет и хранить его там, менять где угодно, и везде он будет одним и тем же... В чем проблема-то?
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
07.02.2016, 16:41
Insite2012, вы можете дать аргументированный ответ на вопрос?
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
07.02.2016, 16:44
Цитата Сообщение от IamRain Посмотреть сообщение
можете дать аргументированный ответ на вопрос?
Непосредственно на ваш? Почему нет, давайте проект. Гадать по кусочкам кода, увы, не умею))) И собирать код по кусочкам тоже.
Кидайте проект, я присоединюсь к вашему исследованию.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
07.02.2016, 16:50
Цитата Сообщение от IamRain Посмотреть сообщение
почему после возрата из диалога Debug.WriteLine в строке 17 выдает true
Потому что в конструкторе ChildForm вы нигде ничего не присваиваете в obj:
C#
1
2
3
4
5
6
7
8
9
10
        public ChildForm(ref DateAndTime obj)
        {
            InitializeComponent();
 
            parentObj = obj; //references are equal
            newObj = obj.DeepClone(); //deep copy made by binary serialization
            
            Date = newObj.Date;
            Time = newObj.Time;
        }
1
07.02.2016, 16:51

Не по теме:

Я уже далеко от ПК - с телефона пишу.

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
07.02.2016, 16:51
Помогаю со студенческими работами здесь

Создание полной копии сервера
поднял полную копию сервера с тем же самим именем тот же домен Lotus domino (только другой ip адрес а так идентично) но когда...

Deep copy and Shadow copy
Этот проект компилируется нормально. И функциональность всех элементов на первый взгляд нормальная. ПРОБЛЕМА: Надо сделать нормально =...

Создание копии объекта используя объявление через указатель
Здравствуйте. Имеется такой код: #include &lt;iostream&gt; #include &lt;cstring&gt; using namespace std; class medicament { private: ...

Вместо полной копии страницы выдает только часть кода (cURL)
Не могу понять, почему у меня вместо полной копии страницы выдает абракадабру (шапку только) $url =...

При запуске макроса с автофигуры с удалением этой же автофигуры с копии - ошибка ActiveSheet.Copy
Добрый день, уважаемые форумчане! Обратиться к вам меня заставила насущная проблема, которую один не могу решить, так что прошу вашей...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! */ #include <iostream> #include <stack> #include <cctype>. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru