Skip to content
Loading
Different Method To Clone An Object
  1. using System;
  2. using System.Reflection;
  3. using System.Collections;
  4. namespace Amir_Harel.Cloning
  5. {
  6. ///
  7. <summary>
  8. /// <b>BaseObject</b> class is an abstract class for you to derive from.
  9. /// Every class that will be dirived from this class will support the <b>Clone</b> method automaticly.
  10. /// The class implements the interface <i>ICloneable</i> and there for every object that will be derived
  11. /// from this object will support the <i>ICloneable</i> interface as well.
  12. /// </summary>
  13. public abstract class BaseObject : ICloneable
  14. {
  15. ///
  16. <summary>
  17. /// Clone the object, and returning a reference to a cloned object.
  18. /// </summary>
  19. /// <returns>Reference to the new cloned object.</returns>
  20. public object Clone()
  21. {
  22. //First we create an instance of this specific type.
  23. object newObject = Activator.CreateInstance(this.GetType());
  24. //We get the array of fields for the new type instance.
  25. FieldInfo[] fields = newObject.GetType().GetFields();
  26. int i = 0;
  27. foreach (FieldInfo fi in this.GetType().GetFields())
  28. {
  29. //We query if the fiels support the ICloneable interface.
  30. Type ICloneType = fi.FieldType.GetInterface("ICloneable", true);
  31. if (ICloneType != null)
  32. {
  33. //Getting the ICloneable interface from the object.
  34. ICloneable IClone = (ICloneable)fi.GetValue(this);
  35. //We use the clone method to set the new value to the field.
  36. fields[i].SetValue(newObject, IClone.Clone());
  37. }
  38. else
  39. {
  40. //If the field doesn't support the ICloneable interface then just set it.
  41. fields[i].SetValue(newObject, fi.GetValue(this));
  42. }
  43. //Now we check if the object support the IEnumerable interface, so if it does
  44. //we need to enumerate all its items and check if they support the ICloneable interface.
  45. Type IEnumerableType = fi.FieldType.GetInterface("IEnumerable", true);
  46. if (IEnumerableType != null)
  47. {
  48. //Get the IEnumerable interface from the field.
  49. IEnumerable IEnum = (IEnumerable)fi.GetValue(this);
  50. //This version support the IList and the IDictionary interfaces to iterate
  51. //on collections.
  52. Type IListType = fields[i].FieldType.GetInterface("IList", true);
  53. Type IDicType = fields[i].FieldType.GetInterface("IDictionary", true);
  54. int j = 0;
  55. if (IListType != null)
  56. {
  57. //Getting the IList interface.
  58. IList list = (IList)fields[i].GetValue(newObject);
  59. foreach (object obj in IEnum)
  60. {
  61. //Checking to see if the current item support the ICloneable interface.
  62. ICloneType = obj.GetType().GetInterface("ICloneable", true);
  63. if (ICloneType != null)
  64. {
  65. //If it does support the ICloneable interface, we use it to set the clone of
  66. //the object in the list.
  67. ICloneable clone = (ICloneable)obj;
  68. list[j] = clone.Clone();
  69. }
  70. //NOTE: If the item in the list is not support the ICloneable interface then
  71. // in the cloned list this item will be the same item as in the original list
  72. //(as long as this type is a reference type).
  73. j++;
  74. }
  75. }
  76. else if (IDicType != null)
  77. {
  78. //Getting the dictionary interface.
  79. IDictionary dic = (IDictionary)fields[i].GetValue(newObject);
  80. j = 0;
  81. foreach (DictionaryEntry de in IEnum)
  82. {
  83. //Checking to see if the item support the ICloneable interface.
  84. ICloneType = de.Value.GetType().GetInterface("ICloneable", true);
  85. if (ICloneType != null)
  86. {
  87. ICloneable clone = (ICloneable)de.Value;
  88. dic[de.Key] = clone.Clone();
  89. }
  90. j++;
  91. }
  92. }
  93. }
  94. i++;
  95. }
  96. return newObject;
  97. }
  98. }
  99. }
Clone with Serialization
  1. using System;
  2. using System.IO;
  3. using System.Runtime.Serialization;
  4. using System.Runtime.Serialization.Formatters.Binary;
  5. public static class ObjectClone
  6. {
  7. public static T Clone<T>(T RealObject)
  8. {
  9. using (Stream objectStream = new MemoryStream())
  10. {
  11. IFormatter formatter = new BinaryFormatter();
  12. formatter.Serialize(objectStream, RealObject);
  13. objectStream.Seek(0, SeekOrigin.Begin);
  14. return (T)formatter.Deserialize(objectStream);
  15. }
  16. }
  17. }
Clone with IL
  1. class Program
  2. {
  3. ///
  4. <summary>
  5. /// Delegate handler that's used to compile the IL to.
  6. /// (This delegate is standard in .net 3.5)
  7. /// </summary>
  8. /// <typeparam name="T1">Parameter Type</typeparam>
  9. /// <typeparam name="TResult">Return Type</typeparam>
  10. /// <param name="arg1">Argument</param>
  11. /// <returns>Result</returns>
  12. public delegate TResult Func<T1, TResult>(T1 arg1);
  13. ///
  14. <summary>
  15. /// This dictionary caches the delegates for each 'to-clone' type.
  16. /// </summary>
  17. static Dictionary<Type, Delegate> _cachedIL = new Dictionary<Type, Delegate>();
  18. ///
  19. <summary>
  20. /// The Main method that's executed for the test.
  21. /// </summary>
  22. /// <param name="args"></param>
  23. static void Main(string[] args)
  24. {
  25. DoCloningTest(1000);
  26. DoCloningTest(10000);
  27. DoCloningTest(100000);
  28. //Commented because the test takes long <img class="emoji" draggable="false" alt="??" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f609.svg">
  29. //DoCloningTest(1000000);
  30. Console.ReadKey();
  31. }
  32. ///
  33. <summary>
  34. /// Do the cloning test and printout the results.
  35. /// </summary>
  36. /// <param name="count">Number of items to clone</param>
  37. private static void DoCloningTest(int count)
  38. {
  39. // Create timer class.
  40. HiPerfTimer timer = new HiPerfTimer();
  41. double timeElapsedN = 0, timeElapsedR = 0, timeElapsedIL = 0;
  42. Console.WriteLine("--> Creating {0} objects...", count);
  43. timer.StartNew();
  44. List<Person> personsToClone = CreatePersonsList(count);
  45. timer.Stop();
  46. Person temp = CloneObjectWithIL(personsToClone[0]);
  47. temp = null;
  48. Console.WriteLine("\tCreated objects in {0} seconds", timer.Duration);
  49. Console.WriteLine("- Cloning Normal...");
  50. List<Person> clonedPersons = new List<Person>(count);
  51. timer.StartNew();
  52. foreach (Person p in personsToClone)
  53. {
  54. clonedPersons.Add(CloneNormal(p));
  55. }
  56. timer.Stop();
  57. timeElapsedN = timer.Duration;
  58. Console.WriteLine("- Cloning IL...");
  59. clonedPersons = new List<Person>(count);
  60. timer.StartNew();
  61. foreach (Person p in personsToClone)
  62. {
  63. clonedPersons.Add(CloneObjectWithIL<Person>(p));
  64. }
  65. timer.Stop();
  66. timeElapsedIL = timer.Duration;
  67. Console.WriteLine("- Cloning Reflection...");
  68. clonedPersons = new List<Person>(count);
  69. timer.StartNew();
  70. foreach (Person p in personsToClone)
  71. {
  72. clonedPersons.Add(CloneObjectWithReflection(p));
  73. }
  74. timer.Stop();
  75. timeElapsedR = timer.Duration;
  76. Console.WriteLine();
  77. Console.ForegroundColor = ConsoleColor.Green;
  78. Console.WriteLine("----------------------------------------");
  79. Console.WriteLine("Object count:\t\t{0}", count);
  80. Console.WriteLine("Cloning Normal:\t\t{0:00.0000} s", timeElapsedN);
  81. Console.WriteLine("Cloning IL:\t\t{0:00.0000} s", timeElapsedIL);
  82. Console.WriteLine("Cloning Reflection:\t{0:00.0000} s", timeElapsedR);
  83. Console.WriteLine("----------------------------------------");
  84. Console.ResetColor();
  85. }
  86. ///
  87. <summary>
  88. /// Create a list of persons with random data and a given number of items.
  89. /// </summary>
  90. /// <param name="count">Number of persons to generate</param>
  91. /// <returns>List of generated persons</returns>
  92. private static List<Person> CreatePersonsList(int count)
  93. {
  94. Random r = new Random(Environment.TickCount);
  95. List<Person> persons = new List<Person>(count);
  96. for (int i = 0; i < count; i++)
  97. {
  98. Person p = new Person();
  99. p.ID = r.Next();
  100. p.Name = string.Concat("Slaets_", r.Next());
  101. p.FirstName = string.Concat("Filip_", r.Next());
  102. persons.Add(p);
  103. }
  104. return persons;
  105. }
  106. ///
  107. <summary>
  108. /// Clone one person object with reflection
  109. /// </summary>
  110. /// <param name="p">Person to clone</param>
  111. /// <returns>Cloned person</returns>
  112. private static Person CloneObjectWithReflection(Person p)
  113. {
  114. // Get all the fields of the type, also the privates.
  115. FieldInfo[] fis = p.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
  116. // Create a new person object
  117. Person newPerson = new Person();
  118. // Loop through all the fields and copy the information from the parameter class
  119. // to the newPerson class.
  120. foreach (FieldInfo fi in fis)
  121. {
  122. fi.SetValue(newPerson, fi.GetValue(p));
  123. }
  124. // Return the cloned object.
  125. return newPerson;
  126. }
  127. ///
  128. <summary>
  129. /// Generic cloning method that clones an object using IL.
  130. /// Only the first call of a certain type will hold back performance.
  131. /// After the first call, the compiled IL is executed.
  132. /// </summary>
  133. /// <typeparam name="T">Type of object to clone</typeparam>
  134. /// <param name="myObject">Object to clone</param>
  135. /// <returns>Cloned object</returns>
  136. private static T CloneObjectWithIL<T>(T myObject)
  137. {
  138. Delegate myExec = null;
  139. if (!_cachedIL.TryGetValue(typeof(T), out myExec))
  140. {
  141. // Create ILGenerator
  142. DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true);
  143. ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
  144. ILGenerator generator = dymMethod.GetILGenerator();
  145. LocalBuilder lbf = generator.DeclareLocal(typeof(T));
  146. //lbf.SetLocalSymInfo("_temp");
  147. generator.Emit(OpCodes.Newobj, cInfo);
  148. generator.Emit(OpCodes.Stloc_0);
  149. foreach (FieldInfo field in myObject.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic))
  150. {
  151. // Load the new object on the eval stack... (currently 1 item on eval stack)
  152. generator.Emit(OpCodes.Ldloc_0);
  153. // Load initial object (parameter) (currently 2 items on eval stack)
  154. generator.Emit(OpCodes.Ldarg_0);
  155. // Replace value by field value (still currently 2 items on eval stack)
  156. generator.Emit(OpCodes.Ldfld, field);
  157. // Store the value of the top on the eval stack into the object underneath that value on the value stack.
  158. // (0 items on eval stack)
  159. generator.Emit(OpCodes.Stfld, field);
  160. }
  161. // Load new constructed obj on eval stack -> 1 item on stack
  162. generator.Emit(OpCodes.Ldloc_0);
  163. // Return constructed object. --> 0 items on stack
  164. generator.Emit(OpCodes.Ret);
  165. myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
  166. _cachedIL.Add(typeof(T), myExec);
  167. }
  168. return ((Func<T, T>)myExec)(myObject);
  169. }
  170. ///
  171. <summary>
  172. /// Clone a person object by manually typing the copy statements.
  173. /// </summary>
  174. /// <param name="p">Object to clone</param>
  175. /// <returns>Cloned object</returns>
  176. private static Person CloneNormal(Person p)
  177. {
  178. Person newPerson = new Person();
  179. newPerson.ID = p.ID;
  180. newPerson.Name = p.Name;
  181. newPerson.FirstName = p.FirstName;
  182. return newPerson;
  183. }
  184. }

Clone with Extension Methods

I found this blog and it’s great. I am providing the link here.

P.S.

There is nothing much new in this article here asI already found similar writing online. I hope this article enlarges the scope of its original article .This article I will keep for my personal knowledge repository