Last week on the train I was reading Design Patterns in C# by Steven John Metsker and came across the Prototype pattern. A short description of the Prototype patern is this (from the front cover of the book): Provide new objects by copying an example. So ok, no problem I get that. As I am reading the chapter and examples, I start thinking about something I was reading the weekend before: C++ copy constructors. Before I mention anything about C++ I want to remind the reader that I am currently a Beginner (notice the capital 'B') in C++ so I don't know a hell of a lot about it.
C++ copy constructors are used when you want to create an object from an already existing object, such as:
51 COutline outline2 = outline;
where the “outline” variable is an already existing object. For those of you who do not use C++, let me try and explain a little of why there is such thing as a copy constructor. C++ objects have the ability to control (override, overload or implement) about everything that you can do (or would ever want to do) with an object, like operators and even a copy constructor which implements the code that will be run when a new object is to be created by an existing object. Interesting thought when you think about it.
.Net has an interface named ICloneable, which (as MSDN says) “Supports cloning, which creates a new instance of a class with the same value as the existing instance”. So the copy constructor and the ICloneable interface sort of have the same reason for existing. The only method on the ICloneable interface is Clone() which returns an Object type. The whole reason I am writing this entry has to do with implementing a prototype pattern ... or should I say creating a new object by copying an “prototype” or “example” object. Actually since that is not that hard ... the real reason I am writing this is to mention what you need to look out for as well as a little information on how copying objects is handled in C++. Lets start with the old and work our way to the new ....
C++ copy constructor example:
4 #include "stdafx.h"
5 #include <iostream>
6 using namespace std;
7
8 class COutline
9 {
10 private:
11 int m_index;
12 char* m_title;
13 public:
14 COutline(int index, char* title) {
15 m_index = index;
16 m_title = new char [strlen(title)+1];
17 strcpy(m_title, title);
18 }
19
20 // copy constructor
21 COutline(const COutline& outline) {
22 m_index = outline.m_index;
23
24 m_title = new char [strlen(outline.m_title)+1];
25 strcpy(m_title, outline.m_title);
26 }
27
28 int Index() {
29 return m_index;
30 }
31
32 char* Title(){
33 return m_title; }
34
35 ~COutline() {
36 delete[] m_title;
37 }
38 };
39
40 int _tmain(int argc, _TCHAR* argv[])
41 {
42
43 COutline outline(1, "title");
44
45 COutline outline2 = outline;
46
47 cout << outline.Title() << endl;
48 cout << outline2.Title() << endl;
49
50 return 0;
51 }
The copy constructor starts on line 21 and goes to line 26. The whole reason for this copy constructor in this case is to copy the string to a new char array for the new object copy ... otherwise both objects will try to hold references to the same char array, which as I am sure you understand would be a bad thing. So ok, so that is C++, who cares right? Well you really need to understand the situation going on here ... there is the same problem in .Net. Just because we now have a garbage collector, a managed environment and Object.MemberwiseClone() is no excuse for not understanding the differences between values, references and how copies (or clones) are created ... because you might get it wrong if you don't.
Now here is a C# class that I will later show the code that does clone two different ways - shallow copy and deep copy. This class is an abstract base class that will be used to create the prototype later or implement the prototype pattern how ever you want to look at it. It is a simple class, an int, a string and an ArrayList.
5 abstract class Outline : ICloneable
6 {
7 private int _index;
8 private string _title;
9
10 public Outline()
11 {}
12
13 public Outline(int index, string title)
14 {
15 _index = index;
16 _title = title;
17 }
18
19 public int Index
20 {
21 get{ return _index; }
22 set{ _index = value; }
23 }
24
25 public string Title
26 {
27 get{ return _title; }
28 set{ _title = value; }
29 }
30
31 abstract public object Clone();
32
33 abstract public ArrayList Attributes
34 { get; }
35 }
First thing you need to know is that System.Object has a method on it called MemberwiseClone(). Next thing you need to know is that the object copy created from this method is a “Shallow Copy”. A shallow copy is a new object that has a copy of all the value objects but the reference objects (ArrayList in class above and technically the string - but I won't go into that) have their references copied to this new object. This gives you the same situation where a copy constructor is needed in C++ (the compiler actually will create a default copy constructor but will only do the shallow copy), the case where you don't want copies of references but new objects and references to those. So if all you have are value types on the object, MemberwiseClone() would work fine (actually strings will work in this situation too). Below is a class that implements the above abstract outline class and implements a shallow copy in its Clone() method.
38 class ConcreteOutline : Outline
39 {
40 private ArrayList _attributes;
41
42 public ConcreteOutline(int index, string title) : base (index, title)
43 {
44 _attributes = new ArrayList();
45 _attributes.Add(index.ToString());
46 _attributes.Add(title);
47 }
48
49 override public object Clone()
50 {
51 // Shallow copy
52 return this.MemberwiseClone();
53 }
54
55 override public ArrayList Attributes
56 {
57 get
58 {
59 return _attributes;
60 }
61 }
62 }
So if you now run some code like the following, you might get some unexpected results if you don't understand what is going on:
100 // shallow copy example
101 ConcreteOutline prototype = new ConcreteOutline(1, "Prototype 1");
102
103 ConcreteOutline coCopy1 = (ConcreteOutline)prototype.Clone();
104 ConcreteOutline coCopy2 = (ConcreteOutline)prototype.Clone();
105
106 // add an item to the attributes to check if they have references to the same arraylist
107 coCopy1.Attributes.Add("Add one more");
108
109 // check if the references are equal and check the counts
110 Console.WriteLine(Object.ReferenceEquals(coCopy1.Attributes,coCopy2.Attributes)); // true
111 Console.WriteLine(coCopy1.Attributes.Count); // 3
112 Console.WriteLine(coCopy2.Attributes.Count); // 3
Since our new object is a shallow copy of the prototype ... the ArrayList for the first object is the same one the second object points to. This is why if you add items to one list, you get it added to the second list ... or actually the same exact list!
In the code below, I try to create a sort of private copy constructor (though I'm not 100% sure it makes since in this case) that aids the Clone() method in returning a deep copy of the prototype.
64 class ConcreteOutline2 : Outline
65 {
66 private ArrayList _attributes;
67
68 public ConcreteOutline2(int index, string title) : base (index, title)
69 {
70 _attributes = new ArrayList();
71 _attributes.Add(index.ToString());
72 _attributes.Add(title);
73 }
74
75 private ConcreteOutline2(ConcreteOutline2 concreteOutline) : base (concreteOutline.Index, concreteOutline.Title)
76 {
77 this._attributes = (ArrayList)concreteOutline.Attributes.Clone();;
78 }
79
80 public override ArrayList Attributes
81 {
82 get
83 {
84 return _attributes;
85 }
86 }
87
88 override public object Clone()
89 {
90 // Deep copy
91 return new ConcreteOutline2(this);
92 }
93 }
So now if you run some code like below, you will get two separate ArrayLists for the two seperate objects:
114 // deep copy example
115 ConcreteOutline2 prototype2 = new ConcreteOutline2(2, "Prototype 2");
116 ConcreteOutline2 co2Copy1 = (ConcreteOutline2)prototype2.Clone();
117 ConcreteOutline2 co2Copy2 = (ConcreteOutline2)prototype2.Clone();
118
119 // add an item to the attributes to check if they have references to the same arraylist
120 co2Copy1.Attributes.Add("Add one more");
121
122 // check if the references are equal and check the counts
123 Console.WriteLine(Object.ReferenceEquals(co2Copy1.Attributes,co2Copy2.Attributes)); // false
124 Console.WriteLine(co2Copy1.Attributes.Count); // 3
125 Console.WriteLine(co2Copy2.Attributes.Count); // 2
So what does this all mean? Well it means you need to be careful when you are copying objects. You need to make sure (like C++ programmers did before you) that you are indeed creating the type of copy you need, either shallow or deep.
Some links for you to check out:
C++ Notes: OOP: Copy Constructors http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
Data & Object Factory: Prototype Pattern: http://www.dofactory.com/Patterns/PatternPrototype.aspx
C# Corner: Prototype Pattern http://www.c-sharpcorner.com/Code/2002/Mar/PrototypePattersAJ.asp