Click here to Skip to main content
Click here to Skip to main content

Base class for cloning an object in C#

, 29 Dec 2002
Rate this:
Please Sign up or sign in to vote.
This class implements the ICloneable for you.

Introduction

Although the subject of cloning in the real world is controversial, in the .NET world it is still safe to use, or isn’t it?

How many times did you find yourself implementing the ICloneable interface for your class, but every time you do the same code, or you do a specific code for each class. And what about, when you add a new field to the class, and you forgot to update the Clone method for the new field. Believe me, this sort of thing leads to annoying bugs.

This is where my class comes to the rescue. With a little help from the reflection mechanism, I created an abstract class that implements the ICloneable interface with the default behavior. Now you are probably asking yourself: What is the default behavior? Well I’m glad you asked. Default behavior for cloning, is to clone every field in the class by the following algorithm:

  1. For each field in the class, ask if it supports the ICloneable interface.
  2. If the field doesn’t support the ICloneable interface, then the field is set in the regular manner, which means that if this field is a value type, then the value will be copied, but if the field is a reference type, the clone field will be pointing to the same object.
  3. If the field supports the ICloneable interface, we use its Clone method to set it in the clone object.
  4. If the field supports the IEnumerable interface, then we need to check if it supports the IList or the IDictionary interface. If it does, then we iterate the collection, and for each item in the collection we ask if it supports the ICloneable interface.

How to use

All you have to do to make your class support the ICloneable interface, is to derive your class from the BaseObject as follow:

public class MyClass : BaseObject
{
    public string myStr =”test”;
    public int id;
}

public class MyContainer : BaseObject
{
    public string name = “test2”;
    public MyClass[] myArray= new MyClass[5];

    public class MyContainer()
    {
        for(int i=0 ; i<5 ; i++)
        {
             this.myArray[I] = new MyClass();
        }
    }
}

Now in the Main method you can do the following:

static void Main(string[] args)
{
    MyContainer con1 = new MyContainer();
    MyContainer con2 = (MyContainer)con1.Clone();

   con2.myArray[0].id = 5;
}

When inspecting the con2 instance you will see that the MyClass instance in the first index was changed to 5, but the con1 instance remained without changes. So you can see that any field you will add to your class, which support the ICloneable interface will be cloned as well. Furthermore, if the field supports the IList interface or the IDictionary interface, the method will detect it and will loop through all the items and will try to clone them as well.

Implementation

/// <summary>
/// BaseObject class is an abstract class for you to derive from.
/// Every class that will be dirived from this class will support the 
/// Clone method automaticly.

/// The class implements the interface ICloneable and there 
/// for every object that will be derived 

/// from this object will support the ICloneable interface as well.
/// </summary>

public abstract class BaseObject : ICloneable
{
    /// <summary>
    /// Clone the object, and returning a reference to a cloned object.
    /// </summary>
    /// <returns>Reference to the new cloned 
    /// object.</returns>
    public object Clone()
    {
        //First we create an instance of this specific type.
        object newObject  = Activator.CreateInstance( this.GetType() );

        //We get the array of fields for the new type instance.
        FieldInfo[] fields = newObject.GetType().GetFields();

        int i = 0;

        foreach( FieldInfo fi in this.GetType().GetFields() )
        {
            //We query if the fiels support the ICloneable interface.
            Type ICloneType = fi.FieldType.
                        GetInterface( "ICloneable" , true );

            if( ICloneType != null )
            {
                //Getting the ICloneable interface from the object.
                ICloneable IClone = (ICloneable)fi.GetValue(this);

                //We use the clone method to set the new value to the field.
                fields[i].SetValue( newObject , IClone.Clone() );
            }
            else
            {
                // If the field doesn't support the ICloneable 
                // interface then just set it.
                fields[i].SetValue( newObject , fi.GetValue(this) );
            }

            //Now we check if the object support the 
            //IEnumerable interface, so if it does
            //we need to enumerate all its items and check if 
            //they support the ICloneable interface.
            Type IEnumerableType = fi.FieldType.GetInterface
                            ( "IEnumerable" , true );
            if( IEnumerableType != null )
            {
                //Get the IEnumerable interface from the field.
                IEnumerable IEnum = (IEnumerable)fi.GetValue(this);

                //This version support the IList and the 
                //IDictionary interfaces to iterate on collections.
                Type IListType = fields[i].FieldType.GetInterface
                                    ( "IList" , true );
                Type IDicType = fields[i].FieldType.GetInterface
                                    ( "IDictionary" , true );

                int j = 0;
                if( IListType != null )
                {
                    //Getting the IList interface.
                    IList list = (IList)fields[i].GetValue(newObject);

                    foreach( object obj in IEnum )
                    {
                        //Checking to see if the current item 
                        //support the ICloneable interface.
                        ICloneType = obj.GetType().
                            GetInterface( "ICloneable" , true );
                        
                        if( ICloneType != null )
                        {
                            //If it does support the ICloneable interface, 
                            //we use it to set the clone of
                            //the object in the list.
                            ICloneable clone = (ICloneable)obj;

                            list[j] = clone.Clone();
                        }

                        //NOTE: If the item in the list is not 
                        //support the ICloneable interface then in the 
                        //cloned list this item will be the same 
                        //item as in the original list
                        //(as long as this type is a reference type).

                        j++;
                    }
                }
                else if( IDicType != null )
                {
                    //Getting the dictionary interface.
                    IDictionary dic = (IDictionary)fields[i].
                                        GetValue(newObject);
                    j = 0;
                    
                    foreach( DictionaryEntry de in IEnum )
                    {
                        //Checking to see if the item 
                        //support the ICloneable interface.
                        ICloneType = de.Value.GetType().
                            GetInterface( "ICloneable" , true );

                        if( ICloneType != null )
                        {
                            ICloneable clone = (ICloneable)de.Value;

                            dic[de.Key] = clone.Clone();
                        }
                        j++;
                    }
                }
            }
            i++;
        }
        return newObject;
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Amir Harel
Web Developer
Israel Israel
No Biography provided

Comments and Discussions

 
QuestionLicense for this class PinmemberMember 317768418-Oct-12 18:46 
BugNice try but implementation got problems [modified] PinmemberZhou Feng31-May-12 23:16 
GeneralRe: Nice try but implementation got problems PinmemberZhou Feng31-May-12 23:41 
GeneralMy vote of 5 Pinmemberpaulpraveen20-Aug-10 22:49 
GeneralStack overflow exception Pinmemberroza_gh22-Jun-09 2:26 
GeneralRe: Stack overflow exception PinmemberErling Hagen21-Feb-11 5:08 
Generaldid'nt really create new objects with the enumerable types for me : now populate them with cloned objects - properties instead of fields as well Pinmemberjean.mourmon25-Mar-09 4:17 
QuestionAbout the System.Collections.DictionaryEntry in IDictionary Pinmemberrobrober24-Dec-08 23:20 
AnswerRe: About the System.Collections.DictionaryEntry in IDictionary Pinmemberrobrober27-Dec-08 18:29 
GeneralGetting InvalidOperationException PinmemberJay Purkayastha29-Sep-08 16:31 
GeneralRe: Getting InvalidOperationException Pinmemberjean.mourmon25-Mar-09 0:49 
GeneralRun-time Speed [modified] PinmemberJeremy F15-Jul-08 8:15 
QuestionGetFields() is returning empty set? PinmemberYogSagot12-Jun-08 8:43 
AnswerRe: GetFields() is returning empty set? PinmemberArthg3-Jul-08 8:13 
GeneralRe: GetFields() is returning empty set? [modified] PinmemberJeremy F15-Jul-08 6:28 
GeneralDunno, but didn't worked for me PinmemberSameers (theAngrycodeR )22-Nov-07 11:28 
GeneralRe: Dunno, but didn't worked for me PinmemberMember 397568510-Dec-07 4:03 
GeneralDon't forget BindingFlags and Generics [modified] Pinmemberk^s15-Nov-07 0:26 
QuestionWhat about generics? PinmemberRichard Lennox16-Jul-07 10:56 
AnswerRe: What about generics? PinmemberChrJaeger25-Apr-08 6:46 
QuestionRe: What about generics? [modified] Pinmemberliaokobe24-Nov-08 21:17 
AnswerRe: What about generics? Pinmembersumitkm25-Jan-09 21:01 
QuestionHow about event handlers? [modified] PinmemberMohammad Hamed12-Aug-06 7:45 
GeneralCopy of base class members [modified] PinmemberNicolas Bonamy25-Jul-06 6:32 
GeneralAnd better still.... Pinmemberduckman (the)21-Mar-06 17:18 
GeneralRe: And better still.... Pinmemberdotnetprgrmmr17-Jul-06 11:40 
GeneralCritial Crash PinmemberArash Sabet8-Feb-06 5:42 
GeneralRe: Critial Crash PinmemberArash Sabet8-Feb-06 7:33 
QuestionRe: Critial Crash PinmemberBernd Kicker29-May-07 5:03 
GeneralRe: Critial Crash PinmemberVasudevan Kannan9-May-11 13:35 
GeneralRe: Critial Crash PinmemberArash Sabet8-Feb-06 11:33 
GeneralEmpty string fields will not work! Pinmembermac8122-Dec-05 6:54 
NewsDeepCloner for Fields and Properties PinmemberRenfield7830-Nov-05 3:08 
GeneralRe: DeepCloner for Fields and Properties PinmemberFluid Media22-Sep-06 6:06 
GeneralRe: DeepCloner for Fields and Properties Pinmemberfshost3-Jan-07 15:56 
GeneralSpecific Pinmembersreejith ss nair12-Sep-05 21:09 
QuestionProblems? Pinmemberimillicit19-Aug-05 11:53 
AnswerRe: Problems? Pinmembermac8122-Dec-05 6:46 
Generalsave Property! Pinmemberpegasus@yahoo.co.jp11-Feb-05 20:34 
Generalorder of foreach. PinsussAnonymous13-Oct-04 1:34 
GeneralInfinite loop PinmemberDavid Anderson18-Aug-04 3:42 
AnswerRe: Infinite loop PinmemberJJames24-Sep-07 9:47 
GeneralGreat article PinmemberCohen Shwartz Oren16-Aug-04 21:16 
GeneralClone array of objects is not working as expected PinmemberBabu Aboobacker E.I11-Aug-04 18:00 
GeneralBug fix: For uninitialized fields in original Pinmembermbearden25-Feb-04 7:31 
GeneralRe: Bug fix: For uninitialized fields in original PinmemberHugoBatista26-Oct-04 10:09 
GeneralIf you want non-public fields cloned... Pinmembermbearden25-Feb-04 7:12 
GeneralIdeas with read-only properties PinsussBogdan Ignat9-Jan-04 5:15 
JokeRe: Ideas with read-only properties Pinmemberastromenix16-Jul-07 5:23 
Generalfunny, I got the same myArray[0]'s value Pinmemberray_linn21-Dec-03 18:01 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 30 Dec 2002
Article Copyright 2002 by Amir Harel
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid