Prototype Design Pattern in C# – A Practical Guide with Examples

Creating objects is one of the most common tasks in any application, but creating complex objects repeatedly can sometimes be expensive, unnecessary, or difficult to manage. This becomes especially relevant when an object requires significant initialization, contains many properties, or depends on configuration that is already available in another object.

The Prototype Design Pattern provides a solution by allowing us to create a new object by copying an existing object, rather than creating and initializing a new instance from scratch. The existing object acts as a prototype, and its copy can then be modified independently as needed.

Prototype is one of the creational design patterns described by the Gang of Four (GoF). In C#, the pattern can be implemented in several ways, including custom cloning methods and mechanisms such as ICloneable, depending on the requirements of the application.

In this article, we’ll explore the Prototype Design Pattern in C#, understand how it works, implement it with practical examples, and look at important concepts such as shallow copying, deep copying, and object cloning. We’ll also discuss when the Prototype pattern is useful and when another object-creation approach may be more appropriate.

What Is the Prototype Design Pattern?

The Prototype Design Pattern is a creational design pattern that allows us to create new objects by copying an existing object, known as the prototype, instead of creating and initializing a new object from scratch.

The main idea behind the prototype design pattern is when we already have an object with the desired state and configuration, we can create a copy of that object and modify the copy as required.

For example, imagine an application that needs to create multiple objects with the same initial configuration. Instead of repeatedly setting each property when creating a new object, we can create and configure one object as a prototype and then clone it whenever a new instance is required.

Conceptually, the process looks like this:

Existing Object (Prototype)
		|
		| Clone
		↓
	New Object
		|
		| Modify as needed
		↓
Independent Object

When Should You Use the Prototype Pattern?

The Prototype Design Pattern is useful when creating an object from scratch is expensive, complex, or repetitive. Instead of going through the complete object-creation process every time, we can create a well-configured object once and use it as a prototype for creating new objects.

  1. When Object Creation Is Expensive

    When creating an object requires significant processing, such as database calls, file operations, network requests, or complex calculations, repeatedly performing these operations can impact application performance.

    With the Prototype pattern, we create the expensive object once and clone it whenever a similar object is needed.

    For example, an application may create a report object that requires loading configuration and formatting information. Instead of rebuilding this configuration for every report, we can create a prototype and clone it whenever a new report is needed.

  2. When Objects Have Complex Initialization

    Some objects require many properties to be configured before they can be used. Creating such objects repeatedly can result in duplicated initialization code.

    The Prototype pattern allows us to configure an object once and then create copies of that configured object.

  3. When You Need Many Similar Objects

    The pattern is particularly useful when an application needs to create many objects that share the same initial state.

    For example, a game might need to create multiple enemies with the same basic characteristics. Instead of configuring every enemy individually, a prototype can be created and cloned.

  4. When You Want to Avoid Repeating Initialization Code

    Without the Prototype pattern, object creation code can become repetitive when many objects share the same configuration.

    With a prototype, the common configuration can be defined once. This can make the object-creation code easier to maintain.

  5. When the Exact Runtime Type Should Be Preserved

    Another benefit of the Prototype pattern is that the code creating an object does not necessarily need to know its concrete type.


When Should You Avoid the Prototype Pattern?

The Prototype pattern isn't always the best choice. If an object is simple and inexpensive to create, cloning can introduce unnecessary complexity.

It can also become more complicated when objects contain many reference-type properties, because we need to decide whether those properties should be shared or copied. This is where the distinction between shallow copy and deep copy becomes important.

Therefore, the Prototype pattern is most valuable when the benefits of cloning such as avoiding expensive initialization or creating many similarly configured objects outweigh the additional complexity of implementing and maintaining cloning logic.

Shallow Copy vs. Deep Copy

When implementing the Prototype Design Pattern, one of the most important decisions is determining how the object should be copied. In C#, copying an object can generally be categorized as a shallow copy or a deep copy.

The difference becomes particularly important when an object contains reference-type properties such as other classes, lists, or collections.

What Is a Shallow Copy?

A shallow copy creates a new object and copies the values of the fields from the original object.

For value types, such as int, double, and bool, the actual values are copied. However, for reference types, the reference is copied rather than the referenced object itself.

As a result, both the original object and the cloned object can point to the same nested reference-type object.

Consider the following example:
</> C#
public class Address
{
	public string City { get; set; }
}
public class Employee
{
  public string Name { get; set; }
  public Address Address { get; set; }
  public Employee ShallowCopy()
  {
    return (Employee)this.MemberwiseClone();
  }
}

Now let's create an employee and clone it:
</> C#
var employee1 = new Employee
{
    Name = "Kailash",
    Address = new Address
    {
        City = "Mumbai"
    }
};

var employee2 = employee1.ShallowCopy();

employee2.Name = "Aarav";
employee2.Address.City = "Pune";

At first glance, we might expect only employee2 to change. However, Address is a reference type, so both employees reference the same Address object.

Therefore:
</> C#
Console.WriteLine(employee1.Name);         // Kailash
Console.WriteLine(employee2.Name);         // Aarav

Console.WriteLine(employee1.Address.City); // Pune
Console.WriteLine(employee2.Address.City); // Pune

Changing employee2.Address.City also changes employee1.Address.City. This is the key characteristic of a shallow copy, the top-level object is copied, but nested reference objects are shared.

What Is a Deep Copy?

A deep copy creates a completely independent copy of the object, including the objects referenced by its reference-type properties.

In other words, the original and cloned objects do not share the same nested objects.

For example:
</> C#
public class Employee
{
    public string Name { get; set; }
    public Address Address { get; set; }

    public Employee DeepCopy()
    {
        return new Employee
        {
            Name = this.Name,
            Address = new Address
            {
                City = this.Address.City
            }
        };
    }
}

Now we can create a deep copy:
</> C#
var employee1 = new Employee
{
    Name = "Kailash",
    Address = new Address
    {
        City = "Mumbai"
    }
};

var employee2 = employee1.DeepCopy();

employee2.Name = "Aarav";
employee2.Address.City = "Pune";

This time, the original object remains unchanged:
</> C#
Console.WriteLine(employee1.Name);         // Kailash
Console.WriteLine(employee1.Address.City); // Mumbai

Console.WriteLine(employee2.Name);         // Aarav
Console.WriteLine(employee2.Address.City); // Pune

The difference can be summarized as follows
FeatureShallow CopyDeep Copy
Creates a new top-level objectYesYes
Copies value-type fieldsYesYes
Copies reference valuesYesYes
Creates new nested reference objectsNoYes
Nested objects are sharedYesNo
Changes to nested objects affect originalPotentiallyNo
Implementation complexityLowerHigher

Implementing Prototype Pattern in C#

Now that we understand the Prototype Design Pattern and the difference between shallow and deep copying, let's see how we can implement the pattern in C#.

The basic idea is to define a prototype that provides a way to create a copy of itself. Clients can then work with the prototype without needing to know the details of how the object is created.

There are several ways to implement the Prototype pattern in C#. A simple and flexible approach is to define a Clone() method and let each class control how its objects are copied.

Using A Simple Prototype Implementation

</> C#
public class Employee
{
    public string Name { get; set; }
    public string Department { get; set; }

    public Employee Clone()
    {
        return new Employee
        {
            Name = this.Name,
            Department = this.Department
        };
    }
}

We can now create an employee and use it as a prototype:
</> C#
var employeePrototype = new Employee
{
    Name = "Bajaj",
    Department = "Engineering"
};

var employee1 = employeePrototype.Clone();
var employee2 = employeePrototype.Clone();

employee1.Name = "Tata";
employee2.Name = "Mahindra";

The prototype contains the common initial state, while each clone can be modified independently.
</> C#
Console.WriteLine(employeePrototype.Name); // Bajaj
Console.WriteLine(employee1.Name);          // Tata
Console.WriteLine(employee2.Name);          // Mahindra

The important point here is that the client doesn't need to repeat the initialization logic for every object.

Using MemberwiseClone() to Implement Prototype

MemberwiseClone() is the built-in mechanism in .NET for creating a shallow copy. It is a protected method provided by System.Object.

Consider the following example:
</> C#
public class Employee
{
    public string Name { get; set; }
    public string Department { get; set; }

    public Employee Clone()
    {
        return (Employee)MemberwiseClone();
    }
}


See the shallow copy section to understand more details.

Using an IPrototype Interface

For a more formal implementation, we can define a prototype interface:
</> C#
public interface IPrototype<T>
{
  T Clone();
}

The Employee class can then implement this interface:
</> C#
public class Employee : IPrototype<Employee>
{
  public string Name { get; set; }
  public string Department { get; set; }
  public Employee Clone()
  {
    return new Employee
    {
      Name = this.Name,
      Department = this.Department
      };
    }
}

Now the intention of the class is clear: Employee supports the Prototype pattern and can create another Employee from itself.

Using an ICloneable Interface

C# also provides the ICloneable interface, which contains a single Clone() method:
</> C#
public interface ICloneable
{
  T Clone();
}

The Employee class can then implement this interface:
</> C#
public class Employee : ICloneable
{
  public string Name { get; set; }
  public string Department { get; set; }
  public object Clone()
  {
    return new Employee
    {
      Name = this.Name,
      Department = this.Department
      };
    }
}

Although ICloneable can be used to implement the Prototype pattern, it has an important limitation: its contract does not clearly specify whether Clone() performs a shallow copy or a deep copy.

For this reason, when designing new code, an explicit method such as Clone() returning the concrete type, or a custom generic prototype interface such as IPrototype<T>, can make the intended behavior clearer.

Using Prototype Registry

In larger applications, prototypes can also be stored in a registry. Instead of creating a prototype every time, the application can maintain a collection of predefined prototypes.

For example:
</> C#
public class ReportRegistry
{
  private readonly Dictionary<string, Report> _reports = new();
  public void Register(string key, Report report)
  {
    _reports[key] = report;
  }
  public Report Create(string key)
  {
    return _reports[key].Clone();
  }
}

We can register predefined report configurations:
</> C#
var registry = new ReportRegistry();

registry.Register(
    "sales",
    new Report
    {
        Format = "PDF",
        IncludeHeader = true,
        IncludeFooter = true
    });

registry.Register(
    "inventory",
    new Report
    {
        Format = "Excel",
        IncludeHeader = true,
        IncludeFooter = false
    });

Now the client can request a new report using a key:
</> C#
var salesReport = registry.Create("sales");
salesReport.Title = "Monthly Sales";

var inventoryReport = registry.Create("inventory");
inventoryReport.Title = "Current Inventory";

The registry hides the details of how the prototypes are stored and cloned.

Summary

The Prototype Design Pattern provides a simple way to create new objects by cloning existing ones instead of creating them from scratch. In C#, we can implement it using a custom Clone() method or MemberwiseClone().

When using this pattern, it is important to understand the difference between shallow and deep copying, especially when objects contain reference-type properties.

Prototype is most useful when creating objects is complex or expensive and we need multiple objects with similar initial configurations.

Thanks

Kailash Chandra Behera

I am an IT professional with over 12 years of experience in the full software development life cycle for Windows, services, and web-based applications using Microsoft .NET technologies.

Previous Post Next Post

نموذج الاتصال