- Like cloning => to duplicate the object
- New operator is considered harmful
Example : Adapting to clone an object which consists of many strings (immutable) is a good idea than creating an object
Shallow: when changing clone, the parent will change too. Same memory location
- MemberwiseClone creates shallow copy
- Copies non-static fields of the object to new object
- Value type: bit by bit copy is performed
- Reference type: reference is copied, not the object
ICloneable Interface:
- Creates a copy of existing object
- Has clone method (one member) which provides support beyond MemberwiseClone
Sample Code Download: PrototypeDesignPattern.zip (Visual Studio 2017 Console App)
Implementing Shallow Copy
Address Class
public class Address
{
public int DoorNumber { get; set; }
public int StreetNumber { get; set; }
public int Zipcode { get; set; }
public string Country { get; set; }
public override string ToString()
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("AddressDetails :: ");
stringBuilder.AppendFormat($"Door : {DoorNumber},");
stringBuilder.AppendFormat($"Street: {StreetNumber},");
stringBuilder.AppendFormat($"ZipCode : {Zipcode.ToString()},");
stringBuilder.AppendFormat($"Country : {this.Country}");
return stringBuilder.ToString();
}
}
Employee Class
Create a partial Employee class, in the next step we will implement ICloneable interface
public partial class Employee
{
public Guid Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public Address AddressDetails { get; set; }
public override string ToString()
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendFormat($" Name : {Name}");
stringBuilder.AppendFormat($" DepartmentId : {DepartmentId}");
if (AddressDetails != null)
{
stringBuilder.AppendFormat($" {AddressDetails.ToString()}");
}
return stringBuilder.ToString();
}
}
Implement ICloneable
Implement ICloneable clone method using MemberwiseClone method
public partial class Employee : ICloneable
{
public object Clone()
{
return this.MemberwiseClone();
}
}
Implement Shallow Copy in Program.cs
Implement ShallowCopy function in program.cs and then call it in the main.
static void Main(string[] args)
{
//Shallow Copy - without address (ref)
ShallowCopy();
Console.ReadLine();
}
public static void ShallowCopy()
{
//John
var empJohn = new Employee
{
Id = Guid.NewGuid(),
Name = "John",
DepartmentId = 150
};
Console.WriteLine(empJohn.ToString());
Console.WriteLine("--------");
//Sam: Clone John
var empSam = (Employee) empJohn.Clone();
empSam.Name = "Sam";
Console.WriteLine(empSam.ToString());
Console.WriteLine("--------");
//Change the John information and then re-print
Console.WriteLine("Changed Johns DepatmentId to 161 from 150");
empJohn.DepartmentId = 161;
Console.WriteLine(empJohn.ToString());
Console.WriteLine(empSam.ToString());
}
Run the program and notice that change in department doesn't change the target.
Now create another method to test address (ref) object and observe the output. Change in address details has propagated to address of target object. This means, MemberwiseClone copies the reference and not the address object itself
static void Main(string[] args)
{
//Shallow Copy - without address (ref)
//ShallowCopy();
//Shallow Copy - with ref
ShallowCopyRef();
Console.ReadLine();
}
public static void ShallowCopyRef()
{
//John
var empJohn = new Employee
{
Id = Guid.NewGuid(),
Name = "John",
DepartmentId = 150,
AddressDetails = new Address
{
DoorNumber = 10,
StreetNumber = 20,
Zipcode = 64113,
Country = "US"
}
};
Console.WriteLine(empJohn.ToString());
Console.WriteLine("--------");
//Sam: Clone John
var empSam = (Employee)empJohn.Clone();
empSam.Name = "Sam";
empSam.DepartmentId = 151;
empSam.AddressDetails.StreetNumber = 21;
empSam.AddressDetails.DoorNumber = 11;
Console.WriteLine(empSam.ToString());
Console.WriteLine("--------");
//Change the John information and then re-print
Console.WriteLine("Modified John information");
empJohn.DepartmentId = 161;
empJohn.AddressDetails.StreetNumber = 30;
empJohn.AddressDetails.DoorNumber = 13;
Console.WriteLine(empJohn.ToString());
Console.WriteLine(empSam.ToString());
Console.WriteLine("--------");
//Change the sam information and then re-print
Console.WriteLine("Modified Sam information");
empSam.DepartmentId = 171;
empSam.AddressDetails.StreetNumber = 50;
empSam.AddressDetails.DoorNumber = 15;
Console.WriteLine(empJohn.ToString());
Console.WriteLine(empSam.ToString());
}
}
Implementing Deep Copy
CloneablePrototype Abstract Class
Implement the custom CloneablePrototype implementation as shown below. You'll need to add Newtonsoft.Json nuget package.
public abstract class CloneablePrototype
{
// Shallow copy
public T Clone()
{
return (T)this.MemberwiseClone();
}
// Deep Copy
public T DeepCopy()
{
string result = JsonConvert.SerializeObject(this);
return JsonConvert.DeserializeObject
}
}
and inherit EmployeeDeep Class from above abstract class
public partial class EmployeeDeep : CloneablePrototype
{
...
}
Implementing Deep Copy in Program.cs
Implement as follows and then look at the output.
static void Main(string[] args)
{
//Shallow Copy - without address (ref)
//ShallowCopy();
//Shallow Copy - with ref
//ShallowCopyRef();
//DeepCopy
DeepCopy();
Console.ReadLine();
}
public static void DeepCopy()
{
//John
var empJohn = new EmployeeDeep
{
Id = Guid.NewGuid(),
Name = "John",
DepartmentId = 150,
AddressDetails = new AddressDeep()
{
DoorNumber = 10,
StreetNumber = 20,
Zipcode = 64113,
Country = "US"
}
};
Console.WriteLine(empJohn.ToString());
Console.WriteLine("--------");
//Sam: Clone John
var empSam = (EmployeeDeep) empJohn.DeepCopy();
empSam.Name = "Sam";
empSam.DepartmentId = 151;
empSam.AddressDetails.StreetNumber = 21;
empSam.AddressDetails.DoorNumber = 11;
Console.WriteLine(empSam.ToString());
Console.WriteLine("--------");
//Change the John information and then re-print
Console.WriteLine("Modified John information");
empJohn.DepartmentId = 161;
empJohn.AddressDetails.StreetNumber = 30;
empJohn.AddressDetails.DoorNumber = 13;
Console.WriteLine(empJohn.ToString());
Console.WriteLine(empSam.ToString());
Console.WriteLine("--------");
//Change the sam information and then re-print
Console.WriteLine("Modified Sam information");
empSam.DepartmentId = 171;
empSam.AddressDetails.StreetNumber = 50;
empSam.AddressDetails.DoorNumber = 15;
Console.WriteLine(empJohn.ToString());
Console.WriteLine(empSam.ToString());
}
Run the application and notice that the change in address details in source object doesn’t impact the Target object.
Now change to Clone in DeepCopy method and observe the output. What do you see?
//Sam: Clone John
var empSam = (EmployeeDeep) empJohn.Clone();