Click here to Skip to main content
15,860,943 members
Articles / Programming Languages / C#

Understanding and Implementing Prototype Pattern in C# - Implementing ICloneable Interface and understanding Shallow and Deep Copy

Rate me:
Please Sign up or sign in to vote.
4.94/5 (19 votes)
11 Dec 2012CPOL4 min read 87.5K   595   44   8
In this article, we saw what is prototype pattern. How can we implement Prototype pattern in C#.

Introduction

This article talks about the prototype pattern, when should it be used. We will then see how the prototype pattern can be implemented in C#. Also, this article will discuss about the shallow copy and deep copy in C# and how can we implement ICloneable interface to implement prototype pattern in a .Net optimized way.

Background

There are many situations in our applications where we want to take an object's copy from the context and then proceed with this copy with some independent set of operations. Prototype pattern is specifically useful in such scenarios where we can simply copy an object from the current application context and then proceed with it independent of the original object.

GoF defines prototype pattern as "Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype." so to visualize this pattern let us look at the class diagram of this pattern.

Image 1

In the above diagram the major players involved are:

  • Prototype: This is an interface or abstract class that defined the method to clone itself.
  • ConcretePrototype: This is the concrete class that will clone itself.
  • Client: The application object that need the cloned copy of the object.

Let us now try to see how we can implement this pattern in C#. 

Using the code

To illustrate this pattern, we need a scenario where we need to clone an existing object. For the sake of simplicity I have taken a rudimentary and hypothetical example of a famous auto theft game.

Lets say we have a protagonist in the game. The protagonist has some statistics to define his playing variables. Now whenever we need to save the game the clone of this object will be taken and serialized on the disk(only for this example, this is seldom the case in real games).

So to get a copy of this Protagonist object the Protagonist object is defined in such a way that cloning it should be possible. So the following abstract class defines the Prototype object that we saw in above class diagram.

C#
public abstract class AProtagonist
{
    int m_health;
    int m_felony;
    double m_money;

    public int Health
    {
        get { return m_health; }
        set { m_health = value; }
    }
   
    public int Felony
    {
        get { return m_felony; }
        set { m_felony = value; }
    }

    public double Money
    {
        get { return m_money; }
        set { m_money = value; }
    }

    public abstract AProtagonist Clone();
}

This interface defines all the vital information required for the player and a clone method so that the object can be cloned. Now lets say we need to spawn a concrete player name CJ in the game. This player should be Cloneable so that whenever we need to save the game we can simply clone the object and intitiate the serialization process asynchronously.

C#
class CJ : AProtagonist
{
    public override AProtagonist Clone()
    {
        return this.MemberwiseClone() as AProtagonist;
    }
}

Now this class is the ConcretePrototype that provides the facility to clone itself. In this example the ConcretePrototype does not contain any specialized members of its own but in some cases this class could contain some member variable or functions of its own.

Now let us see how the client code will be written to clone this object.

C#
static void Main(string[] args)
{
    // The code to demonstrate the classic Prorotype Pattern
    CJ player = new CJ();
    player.Health = 1;
    player.Felony = 10;
    player.Money = 2.0;

    Console.WriteLine("Original Player stats:");
    Console.WriteLine("Health: {0}, Felony: {1}, Money: {2}", 
        player.Health.ToString(), 
        player.Felony.ToString(), 
        player.Money.ToString());

    // We enter the cheat code here and we have a new 
    // player object that we can save on the disk asyncronously.
    CJ playerToSave = player.Clone() as CJ;            

    Console.WriteLine("\nCopy of player to save on disk:");
    Console.WriteLine("Health: {0}, Felony: {1}, Money: {2}", 
        playerToSave.Health.ToString(), 
        playerToSave.Felony.ToString(), 
        playerToSave.Money.ToString());
}
Image 2

Understanding Shallow copy and Deep copy

The above code perfectly illustrates the prototype pattern in action. There is one problem though. We are using the method MemberwiseCopy in our implementation. The problem with the memberwise copy is that it creates a shallow copy of the object i.e. if the object contains any reference types then only the address of that reference type will be copied from source to target and both the versions will keep pointing to the same object.

To illustrate this point lets have a class called AdditionalDetails containing more information about the Protagonist.

C#
public class AdditionalDetails
{
    int m_charisma;
    int m_fitness;

    public int Charisma
    {
        get { return m_charisma; }
        set { m_charisma = value; }
    }
    
    public int Fitness
    {
        get { return m_fitness; }
        set { m_fitness = value; }
    }
}

Now the extended version of our abstract class will contain a member variable for the

AdditionalDetails
object.

C#
public abstract class AProtagonistEx
{
    int m_health;
    int m_felony;
    double m_money;
    // This is a reference type now
    AdditionalDetails m_details = new AdditionalDetails();

    public int Health
    {
        get { return m_health; }
        set { m_health = value; }
    }

    public int Felony
    {
        get { return m_felony; }
        set { m_felony = value; }
    }

    public double Money
    {
        get { return m_money; }
        set { m_money = value; }
    }

    public AdditionalDetails Details
    {
        get { return m_details; }
        set { m_details = value; }
    }

    public abstract AProtagonistEx Clone();
}

And the Concrete class will still perform a clone operation using Memberwisecopy function.

C#
class CJEx : AProtagonistEx
{
    public override AProtagonistEx Clone()
    {
        return this.MemberwiseClone() as AProtagonistEx;
    }
}

Now the problem with this is that after copy both the versions will be pointing to the same object of the AdditionalDetials.

C#
static void Main(string[] args)
{
    // The code to demonstrate the shallow copy
    CJEx playerEx = new CJEx();
    playerEx.Health = 1;
    playerEx.Felony = 10;
    playerEx.Money = 2.0;
    playerEx.Details.Fitness = 5;
    playerEx.Details.Charisma = 5;

    // Lets clone the above object and change the 
    // proprties of contained object
    CJEx shallowClonedPlayer = playerEx.Clone() as CJEx;
    shallowClonedPlayer.Details.Charisma = 10;
    shallowClonedPlayer.Details.Fitness = 10;

    // Lets see what happened to the original object
    Console.WriteLine("\nOriginal Object:");
    Console.WriteLine("Charisma: {0}, Fitness: {1}",
        playerEx.Details.Charisma.ToString(),
        playerEx.Details.Fitness.ToString());
    Console.WriteLine("\nShallow Cloned Object:");
    Console.WriteLine("Charisma: {0}, Fitness: {1}",
        shallowClonedPlayer.Details.Charisma.ToString(),
        shallowClonedPlayer.Details.Fitness.ToString());
}
Image 3

To visualize this problem:

Image 4

To avoid this what we need to do is to create a copy of the internal reference type on the heap and then assign that new object with the copy being returned.

C#
class CJEx : AProtagonistEx
{
    public override AProtagonistEx Clone()
    {
        CJEx cloned = this.MemberwiseClone() as CJEx;
        cloned.Details = new AdditionalDetails();
        cloned.Details.Charisma = this.Details.Charisma;
        cloned.Details.Fitness = this.Details.Fitness;

        return cloned as AProtagonistEx;
    }
}

Now when we run the above client code the internal object's separate copy will be returned.

Image 5

Note: Care must be taken to perform a deep copy because the reference type could still contain reference types internally. The ideal way to do a deep copy is to use reflections and keep copying recursively until the primitive types are reached. For details refer this

So having a shallow copy and deep copy in our object is totally the decision based on the functionality required but we have seen both the ways of doing the copy.

Implement ICloneable interface for Prototype Pattern

The ICloneable interface in C# serves the purpose of defining the clone method in the objects. We can use the ICloneable interface as Prototype(from the above class diagram). So let us see the implementation of the ConcretePrototype by implementing the ICloneable interface.

C#
public class CJFinal : ICloneable
{
    int m_health;
    int m_felony;
    double m_money;
    AdditionalDetails m_details = new AdditionalDetails();

    public int Health
    {
        get { return m_health; }
        set { m_health = value; }
    }

    public int Felony
    {
        get { return m_felony; }
        set { m_felony = value; }
    }

    public double Money
    {
        get { return m_money; }
        set { m_money = value; }
    }

    public AdditionalDetails Details
    {
        get { return m_details; }
        set { m_details = value; }
    }

    private object ShallowCopy()
    {
        return this.MemberwiseClone();
    }

    private object DeepCopy()
    {
        CJFinal cloned = this.MemberwiseClone() as CJFinal;
        cloned.Details = new AdditionalDetails();
        cloned.Details.Charisma = this.Details.Charisma;
        cloned.Details.Fitness = this.Details.Fitness;

        return cloned;
    }

    #region ICloneable Members

    public object Clone()
    {
        return DeepCopy();
    }

    #endregion
}

The client code will now be:

C#
private static void ICloneableVersionCopy()
{
    // Let us see how we can perform the deep copy now
    CJFinal player = new CJFinal();
    player.Health = 1;
    player.Felony = 10;
    player.Money = 2.0;
    player.Details.Fitness = 5;
    player.Details.Charisma = 5;

    // lets clone the object but this time perform a deep copy
    CJFinal clonedPlayer = player.Clone() as CJFinal;
    clonedPlayer.Details.Charisma = 10;
    clonedPlayer.Details.Fitness = 10;

    // Lets see what happened to the original object
    Console.WriteLine("\nOriginal Object:");
    Console.WriteLine("Charisma: {0}, Fitness: {1}",
        player.Details.Charisma.ToString(),
        player.Details.Fitness.ToString());
    Console.WriteLine("\nICloneable Deep Cloned Object:");
    Console.WriteLine("Charisma: {0}, Fitness: {1}",
        clonedPlayer.Details.Charisma.ToString(),
        clonedPlayer.Details.Fitness.ToString());
}
Image 6

Point of interest

In this article, we saw what is prototype pattern. How can we implement Prototype pattern in C#. We looked at the Shallow copy and Deep copy concepts in C# and see how can we implement the ICloneable interface in C#. I hope this has been a little informative.

History 

  • 15 October 2012: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
India India

I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems. IMO, C# is the best programming language and I love working with C# and other Microsoft Technologies.

  • Microsoft Certified Technology Specialist (MCTS): Web Applications Development with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Accessing Data with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Windows Communication Foundation Development with Microsoft .NET Framework 4

If you like my articles, please visit my website for more: www.rahulrajatsingh.com[^]

  • Microsoft MVP 2015

Comments and Discussions

 
GeneralMy vote of 5 Pin
André Poleza9-May-14 2:49
André Poleza9-May-14 2:49 
GeneralMy vote of 5 Pin
ojanacek20-Mar-13 8:42
ojanacek20-Mar-13 8:42 
QuestionJust Curious Pin
Naoya Yamaguchi13-Dec-12 9:55
Naoya Yamaguchi13-Dec-12 9:55 
Would there be anything wrong as a deep-cloning method with the following?
<pre lang="c#">
public CJFinal DeepCopy()
{
var ms = new System.IO.MemoryStream();
var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(ms, this);
ms.Position = 0;
var cloned = (CJFinal)bf.Deserialize(ms);
ms.Close();
return cloned;
}
</pre>
AnswerRe: Just Curious Pin
Rahul Rajat Singh13-Dec-12 16:54
professionalRahul Rajat Singh13-Dec-12 16:54 
AnswerRe: Just Curious Pin
sobo12316-Nov-13 19:07
sobo12316-Nov-13 19:07 
GeneralMy vote of 2 Pin
John B Oliver13-Nov-12 10:14
John B Oliver13-Nov-12 10:14 
BugError on screens Pin
cornikdk23-Oct-12 10:13
cornikdk23-Oct-12 10:13 
GeneralThis is very good article. Pin
Jayesh Sorathia22-Oct-12 4:32
Jayesh Sorathia22-Oct-12 4:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.