Implement Prototype Pattern in C# with MemberwiseClone
This article provides a practical guide on implementing the Prototype
design pattern in C# using the built-in MemberwiseClone
method. You will learn the mechanics of MemberwiseClone,
how to create an interface for cloning, how to perform shallow copies,
and how to address reference types to achieve deep copies when
necessary.
Understanding MemberwiseClone
In C#, the System.Object class provides a protected
method called MemberwiseClone(). This method creates a
shallow copy of the current object. It duplicates non-static fields:
- Value types: Directly copied bit-by-bit.
- Reference types: Only the reference (memory address) is copied, meaning both the original and the clone point to the same object in memory.
Defining the Prototype Interface
While C# includes the ICloneable interface, it returns
an untyped object and does not specify whether the clone is
deep or shallow. A better approach is defining a strongly typed generic
interface:
public interface IPrototype<T>
{
T Clone();
}Implementing a Shallow Copy
To implement a shallow copy using the Prototype pattern, call
MemberwiseClone() inside the concrete class and cast the
result to the concrete type.
public class Employee : IPrototype<Employee>
{
public string Name { get; set; }
public int Age { get; set; }
public Employee(string name, int age)
{
Name = name;
Age = age;
}
public Employee Clone()
{
return (Employee)this.MemberwiseClone();
}
}Usage:
var original = new Employee("Alice", 30);
var clone = original.Clone();
clone.Name = "Bob";
// original.Name remains "Alice", clone.Name is "Bob"Handling Reference Types (Deep Copy)
When a class contains reference-type fields, relying solely on
MemberwiseClone causes modifications in the clone's nested
objects to affect the original object. You can combine
MemberwiseClone with manual copying of reference types to
achieve a deep copy efficiently.
public class Address : IPrototype<Address>
{
public string Street { get; set; }
public string City { get; set; }
public Address(string street, string city)
{
Street = street;
City = city;
}
public Address Clone()
{
return (Address)this.MemberwiseClone();
}
}
public class Customer : IPrototype<Customer>
{
public string FullName { get; set; }
public Address HomeAddress { get; set; }
public Customer(string fullName, Address homeAddress)
{
FullName = fullName;
HomeAddress = homeAddress;
}
public Customer Clone()
{
// Step 1: Perform a shallow copy of primitive/value fields
var clone = (Customer)this.MemberwiseClone();
// Step 2: Manually deep copy reference types
clone.HomeAddress = this.HomeAddress.Clone();
return clone;
}
}Usage:
var originalCustomer = new Customer("John Doe", new Address("123 Main St", "Springfield"));
var clonedCustomer = originalCustomer.Clone();
clonedCustomer.HomeAddress.Street = "456 Elm St";
// originalCustomer.HomeAddress.Street remains "123 Main St"
// clonedCustomer.HomeAddress.Street is "456 Elm St"Summary of Implementation Steps
- Declare a generic interface
IPrototype<T>returning the type itself. - Implement the interface on your class.
- Call
(T)this.MemberwiseClone()to duplicate value-type fields and strings. - If your class has nested reference objects, explicitly reassign them with their own cloned instances to avoid unintended shared references.