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. }