- class Program
- {
- ///
- <summary>
- /// Delegate handler that's used to compile the IL to.
- /// (This delegate is standard in .net 3.5)
- /// </summary>
- /// <typeparam name="T1">Parameter Type</typeparam>
- /// <typeparam name="TResult">Return Type</typeparam>
- /// <param name="arg1">Argument</param>
- /// <returns>Result</returns>
- public delegate TResult Func<T1, TResult>(T1 arg1);
- ///
- <summary>
- /// This dictionary caches the delegates for each 'to-clone' type.
- /// </summary>
- static Dictionary<Type, Delegate> _cachedIL = new Dictionary<Type, Delegate>();
- ///
- <summary>
- /// The Main method that's executed for the test.
- /// </summary>
- /// <param name="args"></param>
- static void Main(string[] args)
- {
- DoCloningTest(1000);
- DoCloningTest(10000);
- DoCloningTest(100000);
- //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">
- //DoCloningTest(1000000);
- Console.ReadKey();
- }
- ///
- <summary>
- /// Do the cloning test and printout the results.
- /// </summary>
- /// <param name="count">Number of items to clone</param>
- private static void DoCloningTest(int count)
- {
- // Create timer class.
- HiPerfTimer timer = new HiPerfTimer();
- double timeElapsedN = 0, timeElapsedR = 0, timeElapsedIL = 0;
- Console.WriteLine("--> Creating {0} objects...", count);
- timer.StartNew();
- List<Person> personsToClone = CreatePersonsList(count);
- timer.Stop();
- Person temp = CloneObjectWithIL(personsToClone[0]);
- temp = null;
- Console.WriteLine("\tCreated objects in {0} seconds", timer.Duration);
- Console.WriteLine("- Cloning Normal...");
- List<Person> clonedPersons = new List<Person>(count);
- timer.StartNew();
- foreach (Person p in personsToClone)
- {
- clonedPersons.Add(CloneNormal(p));
- }
- timer.Stop();
- timeElapsedN = timer.Duration;
- Console.WriteLine("- Cloning IL...");
- clonedPersons = new List<Person>(count);
- timer.StartNew();
- foreach (Person p in personsToClone)
- {
- clonedPersons.Add(CloneObjectWithIL<Person>(p));
- }
- timer.Stop();
- timeElapsedIL = timer.Duration;
- Console.WriteLine("- Cloning Reflection...");
- clonedPersons = new List<Person>(count);
- timer.StartNew();
- foreach (Person p in personsToClone)
- {
- clonedPersons.Add(CloneObjectWithReflection(p));
- }
- timer.Stop();
- timeElapsedR = timer.Duration;
- Console.WriteLine();
- Console.ForegroundColor = ConsoleColor.Green;
- Console.WriteLine("----------------------------------------");
- Console.WriteLine("Object count:\t\t{0}", count);
- Console.WriteLine("Cloning Normal:\t\t{0:00.0000} s", timeElapsedN);
- Console.WriteLine("Cloning IL:\t\t{0:00.0000} s", timeElapsedIL);
- Console.WriteLine("Cloning Reflection:\t{0:00.0000} s", timeElapsedR);
- Console.WriteLine("----------------------------------------");
- Console.ResetColor();
- }
- ///
- <summary>
- /// Create a list of persons with random data and a given number of items.
- /// </summary>
- /// <param name="count">Number of persons to generate</param>
- /// <returns>List of generated persons</returns>
- private static List<Person> CreatePersonsList(int count)
- {
- Random r = new Random(Environment.TickCount);
- List<Person> persons = new List<Person>(count);
- for (int i = 0; i < count; i++)
- {
- Person p = new Person();
- p.ID = r.Next();
- p.Name = string.Concat("Slaets_", r.Next());
- p.FirstName = string.Concat("Filip_", r.Next());
- persons.Add(p);
- }
- return persons;
- }
- ///
- <summary>
- /// Clone one person object with reflection
- /// </summary>
- /// <param name="p">Person to clone</param>
- /// <returns>Cloned person</returns>
- private static Person CloneObjectWithReflection(Person p)
- {
- // Get all the fields of the type, also the privates.
- FieldInfo[] fis = p.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
- // Create a new person object
- Person newPerson = new Person();
- // Loop through all the fields and copy the information from the parameter class
- // to the newPerson class.
- foreach (FieldInfo fi in fis)
- {
- fi.SetValue(newPerson, fi.GetValue(p));
- }
- // Return the cloned object.
- return newPerson;
- }
- ///
- <summary>
- /// Generic cloning method that clones an object using IL.
- /// Only the first call of a certain type will hold back performance.
- /// After the first call, the compiled IL is executed.
- /// </summary>
- /// <typeparam name="T">Type of object to clone</typeparam>
- /// <param name="myObject">Object to clone</param>
- /// <returns>Cloned object</returns>
- private static T CloneObjectWithIL<T>(T myObject)
- {
- Delegate myExec = null;
- if (!_cachedIL.TryGetValue(typeof(T), out myExec))
- {
- // Create ILGenerator
- DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true);
- ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
- ILGenerator generator = dymMethod.GetILGenerator();
- LocalBuilder lbf = generator.DeclareLocal(typeof(T));
- //lbf.SetLocalSymInfo("_temp");
- generator.Emit(OpCodes.Newobj, cInfo);
- generator.Emit(OpCodes.Stloc_0);
- foreach (FieldInfo field in myObject.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic))
- {
- // Load the new object on the eval stack... (currently 1 item on eval stack)
- generator.Emit(OpCodes.Ldloc_0);
- // Load initial object (parameter) (currently 2 items on eval stack)
- generator.Emit(OpCodes.Ldarg_0);
- // Replace value by field value (still currently 2 items on eval stack)
- generator.Emit(OpCodes.Ldfld, field);
- // Store the value of the top on the eval stack into the object underneath that value on the value stack.
- // (0 items on eval stack)
- generator.Emit(OpCodes.Stfld, field);
- }
- // Load new constructed obj on eval stack -> 1 item on stack
- generator.Emit(OpCodes.Ldloc_0);
- // Return constructed object. --> 0 items on stack
- generator.Emit(OpCodes.Ret);
- myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
- _cachedIL.Add(typeof(T), myExec);
- }
- return ((Func<T, T>)myExec)(myObject);
- }
- ///
- <summary>
- /// Clone a person object by manually typing the copy statements.
- /// </summary>
- /// <param name="p">Object to clone</param>
- /// <returns>Cloned object</returns>
- private static Person CloneNormal(Person p)
- {
- Person newPerson = new Person();
- newPerson.ID = p.ID;
- newPerson.Name = p.Name;
- newPerson.FirstName = p.FirstName;
- return newPerson;
- }
- }
Object Cloning is broadly categorized into Shallow copy and Deep copy. This article is not about shallow Copy and deep copy so will just give a brief introduction.
Shallow Copy
Copy the reference but not referenced object.
Deep Copy
Copy the reference and referenced objects.
Multiple ways to create clone of Object,
Clone Manually (ICloneable Interface)This is simple but not ra ecommended way to create object as it does not specify if the object cloning is done by Deep copy approach or Shallow copy approach.
- using System;
- class Rock : ICloneable
- {
- int _weight;
- bool _round;
- bool _mossy;
- public Rock(int weight, bool round, bool mossy)
- {
- this._weight = weight;
- this._round = round;
- this._mossy = mossy;
- }
- public object Clone()
- {
- return new Rock(this._weight, this._round, this._mossy);
- }
- public override string ToString()
- {
- return string.Format("weight = {0}, round = {1}, mossy = {2}",
- this._weight,
- this._round,
- this._mossy);
- }
- }
- class Program
- {
- static void Main()
- {
- Rock rock1 = new Rock(10, true, false);
- Rock rock2 = rock1.Clone() as Rock;
- Console.WriteLine("1. {0}", rock1.ToString());
- Console.WriteLine("2. {0}", rock2.ToString());
- Console.ReadLine();
- }
- }
Clone using MemberwiseClone
- using System;
- class Rock
- {
- int _weight;
- bool _round;
- bool _mossy;
- public Rock(int weight, bool round, bool mossy)
- {
- this._weight = weight;
- this._round = round;
- this._mossy = mossy;
- }
- public Rock ShallowCopy()
- {
- return (Rock)this.MemberwiseClone();
- }
- public override string ToString()
- {
- return string.Format("weight = {0}, round = {1}, mossy = {2}",
- this._weight,
- this._round,
- this._mossy);
- }
- }
- class Program
- {
- static void Main()
- {
- Rock rock1 = new Rock(10, true, false);
- Rock rock2 = rock1.ShallowCopy();
- Console.WriteLine("1. {0}", rock1.ToString());
- Console.WriteLine("2. {0}", rock2.ToString());
- Console.ReadLine();
- }
- }
Note
This is shallow copy using MemberwiseClone. This can be modified a bit to implement deep copy as well.
Join the conversation! Your thoughts help the community grow.