Deep Copy and Shallow Copy

In the “Understanding Reference Types” section, earlier in this chapter, you saw how reference type assignments differ from value type assignments and how assignments are not enough to create a copy of a reference type. You also saw one basic solution to this problem, which involved creating a new instance of a specified reference type and then assigning each property of the target instance with values coming from the original one. But this is not enough, both because it is not complete and because it can be good only with small classes.

To create a complete clone of a reference type, in.NET development, you can take advantage of two techniques: deep copy and shallow copy. Both of these techniques require implementation of the ICloneable interface. Although we discuss interfaces later, the concepts presented here are easy to understand. The ICloneable interface provides a unique method named Clone that enables you to know what classes can be easily cloned. For example, consider the following implementation of the Person class that also implements the ICloneable interface:

Class Person
    Implements ICloneable
    Property FirstName As String
    Property LastName As String
    Property Work As Job
    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone
    End Function
End Class

The most interesting thing in the code is the Clone method required by the ICloneable interface. In the method body, you write code that performs the real copy of the reference type. Fortunately, the Object class provides a method named MemberwiseClone that automatically returns a shallow copy of your reference type. The keyword Me indicates the current instance of the class. Because Clone must work with all possible types, it returns Object. (You’ll see later in this chapter how to convert this result.) The class exposes two String properties, FirstName and LastName. You might remember that although String is a reference type behind the scenes, you actually treat it as a value type. The class also exposes another property, named Work, of type Job. This is a new reference type representing a person’s occupation. Job is implemented as follows:

Class Job
    Property CompanyName As String
    Property Position As String
End Class

Given this implementation, you can simply create a shallow copy.

Shallow Copy

When you create a shallow copy, you create a new instance of the current object and copy values of members of the original to the new one but do not create copies of children (referenced) objects.

Let’s continue the example from the preceding section. Clone creates a copy of the Person class into a new instance and copies members’ values that are value types or Strings. Because Job is a pure reference type, the shallow copy provided by Clone will not also create a clone of Job. (Remember that a shallow copy creates a copy only of the specified instance but not of children objects.) You can easily verify these assertions by writing the following code:

Sub Main()
    'The original person
    Dim firstPerson As New Person
    firstPerson.FirstName = "Alessandro"
    firstPerson.LastName = "Del Sole"
    'Defines a work for the above person
    Dim randomJob As New Job
    randomJob.CompanyName = "Del Sole Ltd."
    randomJob.Position = "CEO"
    'Assignment of the new job
    firstPerson.Work = randomJob
    'Gets a shallow copy of the firstPerson object
    Dim secondPerson As Person = CType(firstPerson.Clone, Person)
    'Check if they are the same instances
    'returns False, 2 different instances:
    Console.WriteLine(firstPerson.FirstName Is secondPerson.FirstName)
    'returns True (still same instance of Job!):
    Console.WriteLine(firstPerson.Work Is secondPerson.Work)
    Console.ReadLine()
End Sub

This code first gets a new instance of the Person class and sets some properties such as a new instance of the Job class, too. Notice how the result of the Clone method, which is of type Object, is converted into a Person instance, using CType. At this point, you can check what happened. The Is operator enables you to compare two instances of reference types and returns True if they are related to the same instance. For the FirstName property, the comparison returns False because the shallow copy creates a new, standalone instance of the Person class. But if you do the same check on the Work property, which is a child reference type of the Person class, the comparison returns True. This means that firstPerson.Work refers to the same instance of the Job class as in secondPerson.Work. And this also means that a shallow copy did not create a new copy of the Job class to be assigned to the secondPerson object. This is where the deep copy comes in.

Deep Copy

Creating a deep copy is a complex way to create a perfect copy of an entire object’s graph. If you need to perform a deep copy, you have some alternatives. The easiest way is to perform a shallow copy of the main object and then manually copy the other properties of child reference types. Later in this chapter, you’ll instead learn about an alternative technique related to serialization, which is discussed in Chapter 39, “Serialization.” At the moment you can focus on editing the previous implementation of the Clone method for performing a simple deep copy. You can also implement the ICloneable interface in the Job class, as follows:

Class Job
    Implements ICloneable
    Property CompanyName As String
    Property Position As String
    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone
    End Function
End Class

Now you can modify the Clone implementation inside the Person class, as follows:

Class Person
    Implements ICloneable
    Property FirstName As String
    Property LastName As String
    Property Work As Job
    Public Function Clone() As Object Implements System.ICloneable.Clone
        Dim tempPerson As Person = CType(Me.MemberwiseClone, Person)
        tempPerson.Work = CType(Me.Work.Clone, Job)
        Return tempPerson
    End Function
End Class


Implementing ICloneable

Implementing ICloneable in referenced classes is not the only way of providing deep copies. You could also generate a new instance of the Job class and manually assign values read from the original instance. But because we are discussing ICloneable and Clone, the preceding example is completed this way.


The code first obtains a shallow copy of the current instance of the Person class and then gets a shallow copy of the child instance of the Job class.


Recursive Cloning

Cloning objects with recursive calls to the Clone method could lead to a stack overflow if the hierarchy of objects is particularly complex. Because of this, the previous implementation goes well with small classes and small object graphs. In other situations, you should instead use serialization.


If you now try to run the same check again, comparing the instances of firstPerson and secondPerson, the output will be as follows:

False
False

This is because now the instances are different. You have two completely standalone instances of the Person class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.82.244