Подтвердить что ты не робот

Почему клонирование (в .NET) так сложно?

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

Почему клонирование не так просто, как дублирование блока памяти, в который помещается объект, и, таким образом, метод Clone в классе object, имеющий все классы в .NET, наследует его?

4b9b3361

Ответ 1

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

Если вы хотите реализовать именно эту функциональность, легко, что для Object.MemberwiseClone(). В большинстве случаев, если имеет смысл клонировать объект (что означает клонированный NetworkStream?), Имеет смысл клонировать каждое свойство... если оно не относится к неизменяемому значению уже и т.д. Другими словами, это очень трудная проблема, поэтому большинство типов не поддерживают клонирование.

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

Ответ 2

Другие говорили об MemberwiseClone, но никто не объяснил, почему он защищен. Я постараюсь дать обоснование.

Проблема заключается в том, что MemberwiseClone просто слепо копирует состояние. Во многих случаях это нежелательно. Например, объект может иметь частное поле, которое является ссылкой на List. Неглубокая копия, например, то, что делает MemberwiseClone, приведет к тому, что новый объект укажет на тот же список - и класс вполне может быть написан, не ожидая, что этот список будет передан кому-либо другому.

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

Или скажите, что у вас есть объект, который открывает сокет или поток файлов, и сохраняет ссылку на него. MemberwiseClone просто скопирует ссылку - и вы можете себе представить, что два объекта, пытающихся чередовать вызовы в один и тот же поток, не заканчиваются хорошо.

Короче говоря, "клонирование" не является четко определенной операцией для произвольных объектов. Тот факт, что memberwise operator= предоставляется для всех классов по умолчанию в С++, является скорее неприятным, так как слишком часто люди забывают его там и не отключают его для классов, для которых копирование не имеет смысла, или опасно (и на удивление много таких классов).

Ответ 3

Существует (по крайней мере) два вида клонирования. Большинство ссылок говорят о мелком и глубоком клонировании, но на самом деле между ними есть оттенки.

Ключевой проблемой является напряженность между "сколько нужно скопировать" и "сколько должно быть разделено".

Рассмотрим объект Order, содержащий ссылки на Customer, a Address и на List of OrderLines.

Если вы хотите Clone() a Order, что связано с этим?

"Просто копирование блока памяти" даст вам новый Order, но тот, который поделился Customer, Address и тем же List от OrderLines. (Помните, что элементы объекта хранятся по ссылке, поэтому, когда вы дублируете блок памяти, вы получаете две ссылки на один и тот же объект).

Очевидно, что вы не хотите делиться List OrderLines между двумя Order s. На самом деле вы, вероятно, захотите также клонировать каждый OrderLine.

Если вы работали с методом Clone() общего назначения, как бы этот метод знал, какие члены должны быть рекурсивно клонированы, а какие?

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

Последнее замечание: даже когда я создаю возможность объектов Clone(), я не склонен создавать метод Clone(), вместо этого предпочитая конструктор copy - конструктор, который принимает другой объект в качестве основы. Если вы не можете найти Clone(), найдите это.

Ответ 4

Существует такая вещь, как Object.MemberwiseClone(),, которая делает то, что вы описываете. Он делает мелкую копию объекта.

Он не будет делать глубокий клон автоматически... вам нужно будет вызвать клон на всех объектах-членах вручную и т.д.

Ответ 5

Вы должны явно реализовать интерфейс ICloneable в своих классах.

Однако, как заявляет документация, механизм клонирования в MemberwiseClone не различает мелкую и глубокую копию.