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

  1. Declare a generic interface IPrototype<T> returning the type itself.
  2. Implement the interface on your class.
  3. Call (T)this.MemberwiseClone() to duplicate value-type fields and strings.
  4. If your class has nested reference objects, explicitly reassign them with their own cloned instances to avoid unintended shared references.