.NET & C# Development · Lesson 33 of 229
Prototype — Clone Instead of Rebuild
Prototype — Clone Instead of Rebuild
The Prototype pattern creates new objects by copying an existing object (the prototype) rather than constructing from scratch. Use it when creating a new instance is expensive or complex.
The Problem
// Expensive to create — hits database, loads config, initialises subsystems
public class ReportTemplate
{
public string Title { get; set; } = "";
public string Layout { get; set; } = "";
public List<ReportSection> Sections { get; set; } = new();
// Expensive constructor — loads from database
public ReportTemplate(string templateId)
{
var data = LoadFromDatabase(templateId); // slow
Title = data.Title;
Layout = data.Layout;
Sections = data.Sections;
}
}
// Creating 100 reports from the same template:
// BAD — 100 database calls
for (int i = 0; i < 100; i++)
var t = new ReportTemplate("annual-report"); // slow × 100Deep Clone Implementation
public class ReportSection
{
public string Heading { get; set; } = "";
public string Body { get; set; } = "";
public ReportSection Clone() => new()
{
Heading = Heading, // string is immutable — reference copy is fine
Body = Body,
};
}
public class ReportTemplate
{
public string Title { get; set; } = "";
public string Layout { get; set; } = "";
public List<ReportSection> Sections { get; set; } = new();
// Deep clone — new list with cloned sections
public ReportTemplate Clone()
{
return new ReportTemplate
{
Title = Title,
Layout = Layout,
Sections = Sections.Select(s => s.Clone()).ToList(), // deep copy
};
}
}
// Cache one loaded template, clone for each report
var template = LoadOnce("annual-report");
for (int i = 0; i < 100; i++)
{
var report = template.Clone(); // fast — no DB call
report.Title = $"Annual Report {DateTime.UtcNow.Year} — Division {i}";
// customise and use...
}Shallow vs Deep Copy
// Shallow copy: copies references — both original and clone share nested objects
var original = new ReportTemplate { Title = "Original" };
original.Sections.Add(new ReportSection { Heading = "Intro" });
// MemberwiseClone() — shallow
var shallow = (ReportTemplate)original.MemberwiseClone();
shallow.Title = "Copy"; // only changes copy's title ✓
shallow.Sections[0].Heading = "Modified"; // ALSO changes original's section ✗
// original.Sections[0].Heading is now "Modified" — unintended!
// Deep copy: new instances for all nested objects
var deep = original.Clone();
deep.Sections[0].Heading = "Independent"; // original unchanged ✓Prototype Registry
// Registry — store and retrieve named prototypes
public class TemplateRegistry
{
private readonly Dictionary<string, ReportTemplate> _templates = new();
public void Register(string name, ReportTemplate template)
=> _templates[name] = template;
public ReportTemplate Create(string name)
{
if (!_templates.TryGetValue(name, out var proto))
throw new KeyNotFoundException($"Template '{name}' not registered");
return proto.Clone();
}
}
// Set up once at startup
var registry = new TemplateRegistry();
registry.Register("annual", LoadTemplate("annual-report"));
registry.Register("quarterly",LoadTemplate("quarterly-report"));
// Fast clones anywhere in the app
var report = registry.Create("annual");Modern Alternative: Records with with
// Immutable records make prototype trivial
public record OrderTemplate(
string CustomerName,
string DeliveryAddress,
decimal DiscountRate,
List<string> DefaultItems
);
var standard = new OrderTemplate(
CustomerName: "Standard Customer",
DeliveryAddress: "TBD",
DiscountRate: 0.0m,
DefaultItems: new() { "SKU-001", "SKU-002" }
);
// Non-destructive mutation — creates a new instance with one field changed
var loyal = standard with { DiscountRate = 0.10m };
var vip = standard with { DiscountRate = 0.20m };
// Note: DefaultItems is still shared (shallow) — deep copy manually if mutating itInterview Answer
"The Prototype pattern creates new objects by cloning an existing one rather than constructing from scratch — useful when construction is expensive (database load, complex initialisation) or when you need many similar objects with small variations. In C#, implement a
Clone()method that returns a deep copy — don't useMemberwiseClone()for objects with nested reference types, as it creates a shallow copy that shares nested objects. A Prototype Registry caches named prototypes and hands out clones on demand — one DB load, unlimited fast copies. Modern C# records withwith { }syntax are a built-in lightweight prototype for immutable data:var vip = standardOrder with { DiscountRate = 0.20m }creates a new record instance. The trade-off: deep cloning of complex graphs can be tricky and performance-sensitive — test and benchmark."