| Larkware |
| We get up early so that you don't have to. |
By Mike Gunderloy
Thursday, April 17, 2003I had someone write to me yesterday asking for an example of using ICloneable.Clone in a Visual Basic .NET class. Well, here's a quick example, for anyone else who hasn't dug into this corner of the .NET world. I'll start with a simple class hierarchy, in which one recipe can contain many ingredients:
Public Class Recipe
Public Name As String
Public Ingredients As IngredientCollection
Public Sub New()
Ingredients = New IngredientCollection
End Sub
End Class
Public Class Ingredient
Public Name As String
End Class(I'm leaving out the boring details of implementing the strongly-typed collection; I used CodeSmart to do it for me anyhow).
Now, let's add a method to the Recipe class that uses the built-in Object.MemberwiseClone method:
Public Function ShallowCopy() As Recipe
Return Me.MemberwiseClone
End FunctionWith that in place, I can create a form to make what's called a "shallow copy" of an object. Here's some code to demonstrate:
Private Sub btnShallow_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnShallow.Click
' Clear the listbox
lbRecipes.Items.Clear()
' Create a new recipe
Dim R1 As Recipe = New Recipe
R1.Name = "Ham Sandwich"
Dim I1 As Ingredient = New Ingredient
I1.Name = "Ham"
Dim I2 As Ingredient = New Ingredient
I2.Name = "Bread"
R1.Ingredients.Add(I1)
R1.Ingredients.Add(I2)
' Make a shallow copy
Dim R2 As Recipe = R1.ShallowCopy()
R2.Name = "Cheese Sandwich"
R2.Ingredients(0).Name = "Cheese"
' Show the results
DumpRecipe(R1)
DumpRecipe(R2)
End SubAnd here are the results:
Note the problem here. Although the new Recipe object has its own properties (so its name can be set independently), it shares pointers to subsidiary objects with the original Recipe object. Thus, changing an ingredient name in the second recipe changes the corresponding ingredient name in the original recipe. This is probably not what you want when you make a copy of an object.Enter ICloneable. The purpose of the ICloneable interface is to provide a single method, Clone, which makes a "deep copy" of an object: one which has all the properties of the original object, but which is completely disconnected from it. Of course, like any other interface, you need to implement the details yourself. Here's a revised Recipe class implementing ICloneable:
Public Class Recipe
Implements ICloneable
Public Name As String
Public Ingredients As IngredientCollection
Public Sub New()
Ingredients = New IngredientCollection
End Sub
Public Function ShallowCopy() As Recipe
Return Me.MemberwiseClone
End Function
Public Function Clone() As Object Implements System.ICloneable.Clone
' Start with a shallow copy
Dim R As Recipe = Me.MemberwiseClone
' But now give it a new Ingredients collection
R.Ingredients = New IngredientCollection
' And populate it from the original
Dim INew As Ingredient
For Each I As Ingredient In Me.Ingredients
INew = New Ingredient
INew.Name = I.Name
R.Ingredients.Add(INew)
Next
Return R
End Function
End ClassNote that I'm using the new VB .NET 2003 syntax on the For Each loop; if you're using 2002, you'll need to define the loop variable before the loop. Here's the test code:
Private Sub btnDeep_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnDeep.Click
' Clear the listbox
lbRecipes.Items.Clear()
' Create a new recipe
Dim R1 As Recipe = New Recipe
R1.Name = "Ham Sandwich"
Dim I1 As Ingredient = New Ingredient
I1.Name = "Ham"
Dim I2 As Ingredient = New Ingredient
I2.Name = "Bread"
R1.Ingredients.Add(I1)
R1.Ingredients.Add(I2)
' Make a deep copy
Dim R2 As Recipe = R1.Clone()
R2.Name = "Cheese Sandwich"
R2.Ingredients(0).Name = "Cheese"
' Show the results
DumpRecipe(R1)
DumpRecipe(R2)
End SubAnd here are the results:
All better! Here's the VS .NET 2003 project if you'd like to avoid cutting and pasting the code.
Generated using PrettyCode.Encoder Mike Gunderloy is the lead developer for Larkware and author of numerous books and articles on programming topics.
For a deeper look at some of the issues involved in copying and cloning, along with sample code in C#, take a look at Shawn Van Ness's article Copying, Cloning, and Marshalling in .NET on ONDotnet.