Technology Microsoft Software & solutions

Understanding "Shallow Copy" and "Deep Copy" Coding (VB.NET)



You will find this pair of terms quite frequently in articles about programming:

Shallow Copy

and

Deep Copy

This article explains what they mean and shows you how to code both of them. But first: Basic Concepts!

Shallow Copy versus Deep Copy - What's the difference?

None of this really makes a difference unless you're dealing with objects as opposed to fundamental types. A more technical (and also more accurate) way to say this is that shallow copy versus deep copy is only meaningful with "reference types" and not with "value types".

Value types are stored on the directly on the "stack" - a section of memory. Reference types are objects stored on the "managed heap". They get their name because the variable actually consists of a pointer in memory - a reference - to the location of the object on the heap. I cover all this in the short article Nullable Types Give Visual Basic the Ability to Say Nothing.

When you copy a value type, such as an Integer, you get what you expect: a copy. But a reference type is actually just a pointer to an area in memory; a "reference". And it can contain pointers to other areas in memory and so on. You might get a shallow copy, a deep copy, or just another pointer to the same memory location depending on the objects and methods you use. Here's a diagram of what a shallow copy looks like.

--------
Click Here to display the illustration
--------


And here's what a deep copy looks like. Depending on the references in the original object, a deep copy could include quite a lot.

--------
Click Here to display the illustration
--------


Since a shallow copy involves less work, you won't be surprised to learn that making a shallow copy is easy. Just use the MemberwiseClone function. It's so basic that it's a built in method in System.Object, so it's available all the time. Here's an example.

First, we need some objects to work with:

Public Class OriginalObjectProperty ReferredObject1 As ObjectProperty ReferredObject2 As ComplexObjectEnd ClassPublic Class ComplexObjectProperty ReferredObject3 As ObjectEnd Class
These were coded to match the illustrations above, but to actually make it happen, the objects have to be instantiated and initialized.

Public myOriginalObject As New OriginalObjectPublic myComplexObject As New ComplexObject
and

myOriginalObject.ReferredObject1 = "Orig RefObj1 Info"myComplexObject.ReferredObject3 = "Orig RefObj3 Info"myOriginalObject.ReferredObject2 = myComplexObject
To make a shallow copy, create a new object and call the MemberwiseClone function. This example shows the content of the objects before the shallow copy. Then a new value is placed in the copied objects. Finally, the content of the original and the copy is displayed to show that when the shallow copy is changed, the original changes too. They're pointing at the same object.

Dim ShallowCopy As New OriginalObjectConsole.WriteLine("ReferredObject1: " &myOriginalObject.ReferredObject1)Console.WriteLine("ReferredObject3: " &myComplexObject.ReferredObject3)ShallowCopy = DirectCast(myOriginalObject, OriginalObject)ShallowCopy.ReferredObject1 = "Shallow RefObj1 Info"ShallowCopy.ReferredObject2.ReferredObject3 ="Shallow RefObj3 Info"Console.WriteLine(vbCrLf & "myOriginalObject contents ...")Console.WriteLine("ReferredObject1: " &myOriginalObject.ReferredObject1)Console.WriteLine("ReferredObject3: " &myOriginalObject.ReferredObject2.ReferredObject3)Console.WriteLine(vbCrLf & "... and the shallow copy contents ...")Console.WriteLine("ReferredObject1: " &ShallowCopy.ReferredObject1)Console.WriteLine("ReferredObject3: " &ShallowCopy.ReferredObject2.ReferredObject3)' ReferredObject1: Orig RefObj1 Info' ReferredObject3: Orig RefObj3 Info' myOriginalObject contents ...' ReferredObject1: Shallow RefObj1 Info' ReferredObject3: Shallow RefObj3 Info' ... and the shallow copy contents ...' ReferredObject1: Shallow RefObj1 Info' ReferredObject3: Shallow RefObj3 Info
This can be a bug if you're not clear about what you're doing. I documented this in the article Copying Reference Types.

A deep copy is another matter. There are several different techniques that can be used. In fact, although a deep copy is normally assumed to copy the primary object and the entire tree of referenced objects that might be present, the most comprehensive definition of "deep copy" is simply "not shallow". Since you implement the copy in your own code, you might write code that copies some referenced objects but not others. If you're looking at code that is supposed to make a deep copy, don't make any assumptions. When in doubt, check the code to see what it does.

The easiest (not always the best) way to make a deep copy is the "serialization" method. (The two classes, OriginalObject and ComplexObject have to be marked as <Serializable()> as well.

Using stream As New System.IO.MemoryStream()Dim formatter As New_System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()Dim DeepCopy As New OriginalObjectConsole.WriteLine("ReferredObject1: " &myOriginalObject.ReferredObject1)Console.WriteLine("ReferredObject3: " &myComplexObject.ReferredObject3)formatter.Serialize(stream, myOriginalObject)stream.Position = 0DeepCopy = formatter.Deserialize(stream)DeepCopy.ReferredObject1 = "Deep RefObj1 Info"DeepCopy.ReferredObject2.ReferredObject3 = "Deep RefObj3 Info"Console.WriteLine(vbCrLf & "myOriginalObject contents ...")Console.WriteLine("ReferredObject1: " &myOriginalObject.ReferredObject1)Console.WriteLine("ReferredObject3: " &myOriginalObject.ReferredObject2.ReferredObject3)Console.WriteLine(vbCrLf & "... and the deep copy contents ...")Console.WriteLine("ReferredObject1: " &DeepCopy.ReferredObject1)Console.WriteLine("ReferredObject3: " &DeepCopy.ReferredObject2.ReferredObject3)' ReferredObject1: Orig RefObj1 Info' ReferredObject3: Orig RefObj3 Info' myOriginalObject contents ...' ReferredObject1: Orig RefObj1 Info' ReferredObject3: Orig RefObj3 Info' ... and the deep copy contents ...' ReferredObject1: Deep RefObj1 Info' ReferredObject3: Deep RefObj3 InfoEnd Using
In this case, the original stays the same, but the copy still changes, indicating that they're separate objects. If you need to know more about serializing, try this article: All About Serializing in Visual Basic.

One reason that the serialize method might not be right for your code is that it's a relatively expensive way to do the job. If you know that the constructor for the object fully defines it (that is, the New method), then you can just call New on the object and populate it with whatever you need. You can use .NET Reflection methods (such as the FieldInfo method) to recurse through the original object and create an independent copy. This method has the advantage that you can examine, and even process the copied object as you create it. An example showing recursion can be found in Permutations, Recursion, and Traversing a Binary Tree

Leave a reply