65.9K
CodeProject is changing. Read more.
Home

Generics and Cloning

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.83/5 (12 votes)

Jun 12, 2006

CPOL

3 min read

viewsIcon

54036

downloadIcon

266

How to clone generic/template objects

Sample Image - 0g_GenericsAndCloning.jpg

Introduction

In many applications, there is the need to track changes to the data presented to users. There are several approaches in achieving this. In this article, I cover an approach in which I use Cloning and Generics to be able to compare objects and determine if any changes have been made. The use of cloning is important because in some systems the instantiation of an object could be somewhat expensive. Cloning allows us to do a deep copy of the object already instantiated thus preventing the expensive process. Once the object is cloned, we can modify the cloned copy with the new information provided by the user and determine what changes have been made. In order to compare the objects, we need to override the Equals method from the Object class. The challenge with cloning is that we would like to know the object type and minimize the code sections required to do the cloning. In short, we would like to create a base class from which we can clone any object type. This is where Generics can help us.

Background

To better understand this article, I recommend that the readers be somewhat familiar with the following concepts:

  • Generics
  • Cloning/ICloneable

The code for this article was written using .NET 2.0.

Using the Code

The first step is to create the CloneBase class (CloneBase.cs) which implements the ICloneable interface. This is needed to create a copy of object. Any class that implements this interface should also have the XML attribute [Serializable]. Not adding this attribute causes an exception to occur. This attribute must also be added to all the classes that inherit from the base class we are now creating. This takes care of the cloning part of the equation. Now, we need to be able to support different types. In order to do that, we need to declare the class as a template/generic definition by adding the generic type parameter <T>.

 [Serializable]
 public class CloneBase<T> : ICloneable 
 {
   public CloneBase() { } 
   ...  

The generic type parameter is needed because this is what is used to replace where the concrete type would be. In other words, when we need to return a clone of a certain type, T is replaced by such type. We will be able to see this with the implementation of the Clone method. Notice how this method has a return type of T.

public T Clone()
{
      T clone = default(T);        //initialize to default not null
      try
      {
            BinaryFormatter bf = new BinaryFormatter();     //helper to serialize
            MemoryStream memStream = new MemoryStream();    
            bf.Serialize(memStream, this);
            memStream.Flush();
            memStream.Position = 0;
            clone = ((T)bf.Deserialize(memStream));  //this returns the copy of type T
      }
       catch (Exception ex)
      {
            m_status = ex.Message;
      }

       return clone;
}

This code serializes the object into a memory stream, which creates a copy of the object. It then deserializes the generic object by using the line of code below. We can notice the casting done using the generic type (T). The new object is then returned.

clone = ((T)bf.Deserialize(memStream));   //this returns the copy of type T   

We can now take a look at the client code which uses this base class. The project associated with this article has a Product class (Product.cs), which is used to represent a generic product. The class inherits from the CloneBase base class. Notice how this class too must have the XML attribute [Serializable]. This class also overrides the Equals method from the Object class. This is done to implement our own object comparison logic. In this case, we are comparing the value of all the data members. If any of the values is not the same, the code returns false.

[Serializable]
 class Product : CloneBase<Product>
 {
        //data members declared here

        public override bool Equals(object obj)
        {
            bool bRtn = true;

            if (obj != null)
            {
                Product objRef = (Product)obj;

                //simple object type comparison
                if (this.Name != objRef.Name ||  
			this.Description != objRef.Description ||
                    	this.Sku != objRef.Sku || this.Count != objRef.Count)
                    bRtn = false;
            }
            else
                bRtn = false;

            return bRtn;
        }
}

We can now take a look at Windows form created for this article. This simple form displays all the data values for a Product object. When pressing the Save button, the application creates a clone of the object, assigns the values from the form to the clone, and compares the original object with the clone.

private void btnSave_Click(object sender, EventArgs e)
{
            Product clone = _prod.Clone();                //create clone

            if (clone != null)
            {
                UIValues(clone, false);            //get current UI values;
                
                //check to see if any changes
                if (_prod.Equals(clone) == true)
                    MessageBox.Show("There is no change", this.Text); 
                else
                    MessageBox.Show("There is a change", this.Text); 
            }
            else
            {
                //did not clone!!! Use status to check for errors
                MessageBox.Show(_prod.Status, this.Text); 
            }
}

Points of Interest

I have found the use of generics to be very useful. This was a great new feature of .NET 2.0. There are also many generic list classes which we can use to eliminate the use of classes like the ArrayList. I hope this article is useful to some of you. I look forward to getting your feedback.

History

  • 0g06012006: Initial version