Иллюстрированный самоучитель по VB.NET

       

MemberWiseClone


В программировании, как и в современной науке:

  • Клоном называется точная копия объекта.

  • Состояние клона может измениться и стать отличным от состояния исходного объекта.

    Но самое важное правило клонирования формулируется так:

  • Изменения в клоне не должны отражаться на исходном объекте, на основе которого клон создавался.

    Последнее обстоятельство затрудняет клонирование во всех языках ООП, поэтому ме-тод MemberWiseClone считается потенциально опасным. Дело в том, что объект может содержать другие объекты. Если внутренние объекты не будут клонированы одновременно с объектом, их содержащим, вместо пары оригинал-клон вы получите сиамских близнецов, которые будут зависеть друг от друга. Если класс содержит поля, которые представляют собой изменяемые объекты, метод MemberWiseClone заведомо создает «сырой», неполноценный клон (это называется поверхностным копированием). Метод MemberWiseClone успешно клонирует только те объекты, поля которых относятся исключительно к структурным типам.

    Следующий пример наглядно показывает, что имеется в виду под этим предупреждением. Массивы VB .NET в отличие от массивов VB6 являются объектами.

    Допустим, мы пытаемся клонировать объект класса, одно из полей которого представляет собой массив:

    1 Public Class EmbeddedObjects

    2 Private m_Data() As String

    3 Public Sub New(ByVa1 anArray() As String)

    4 m_Data = anArray

    5 End Sub



    6 Public Sub OisplayData()

    7 Dim temp As String

    8 For Each temp In m_Data

    9 Console.WriteLine(temp)

    10 Next

    11 End Sub

    12 Public Sub ChangeData(ByVal newData As String)

    13 m_Data(0) = newData

    14 End Sub

    15 Public Function Clone() As EmbeddedObjects

    16 Return CType(Me.MemberwiseClone. EmbeddedObjects)

    17 End Function

    18 End Class

    Выполните следующую процедуру Sub Main:

    Sub Main()

    Dim anArray() As String ={"HELLO"}

    Dim a As New EmbeddedObjects(anArray)

    Console.WriteLinet"Am going to display the data in object a now!")

    a.DisplayData()



    Dim b As EmbeddedObjects

    b =a.Clone()

    Dim newData As String ="GOODBYE"

    b.ChangeData(newData)

    Console.WriteLine(" Am going to display the data in object b now!")

    b.DisplayData()

    Console.WriteLine("Am going to re-display the data in a" & _

    "after making a change to object b!!!") a.DisplayData()

    Console. ReadLine() End Sub



    Рис. 5.6. Метод MemberWiseClose не работает

    Как видно из рис. 5.6, результат получился весьма неожиданным: изменения клона отражаются на исходном объекте!

    Что происходит в этом примере? Почему метод MemberWiseClone не работает, как задумано? Почему изменения в объекте b отражаются на объекте а? Потому что в строках 2 и 4 класса EmbeddedObjects в качестве значения поля, задаваемого в конструкторе, используется массив. Массивы являются изменяемыми объектами; как было показано в главе 3, из этого следует, что содержимое массива может изменяться даже при передаче по значению (ByVal). Состояние внутреннего массива изменяется в строках 12-14 класса EmbeddedObjects. Поскольку объект и псевдоклон связаны ссылкой на массив m_Data, изменения клона отражаются на исходном объекте.

    Решение этой проблемы рассматривается в разделе «ICloneable» этой главы. А пока мы просто укажем, что настоящий клон (иногда называемый глубокой копией) создает клоны всех полей объекта, при необходимости выполняя рекурсивное кло-нирование. Например, если одно из полей класса является объектом и содержит еще один внутренний объект, процесс клонирования должен опуститься на два уровня в глубь.

    Также существует хитроумная методика клонирования, основанная на сериализации объектов. Подробности приведены в главе 9.

    Наконец, в качестве средства дополнительной защиты разработчики .NET Framework объявили MemberWiseClone защищенным методом класса Object. Как было показано выше, это означает, что MemberWi seCI one может вызываться только из производных классов. Код за пределами производного класса не может клонировать объекты при помощи этого небезопасного метода.Также обратите внимание на то, что MemberWi seCIone возвращает тип Object, поэтому в строке 1б класса EmbeddedObjects приходится использовать функцию СТуре.




    Содержание раздела