Object Cloning Using Reflection

Today on Alastair Patrick’s blog he shared a way to use reflection to clone objects which I found really interesting. I took the liberty of expanding the example a little so I could better understand the effects deep and shallow cloning had. I also replaced his main() with a NUnit test.

using System;

using System.Reflection;

using NUnit.Framework;

public class DeepCopyAttribute : Attribute

{

}

public class PrototypeCloner

{

public object Clone( object prototype )

{

object clone = CreateNewInstance( prototype.GetType() );

CopyPropertyValuesFromPrototype( clone, prototype );

return clone;

}

private object CreateNewInstance(Type type)

{

ConstructorInfo defaultConstructor = type.GetConstructor( new Type[0] );

return defaultConstructor.Invoke(new object[0]);

}

private bool IsPropertyDeepCopied( PropertyInfo property )

{

if( property.GetCustomAttributes( typeof(DeepCopyAttribute), true).Length

!= 0 )

{

return true;

}

else

{

return false;

}

}

private void CopyPropertyValuesFromPrototype(object clone, object prototype)

{

foreach (PropertyInfo property in prototype.GetType().GetProperties())

{

if (IsPropertyDeepCopied(property))

{

object prototypeProperty = property.GetValue(prototype, null);

object cloneProperty = Clone(prototypeProperty);

property.SetValue(clone, cloneProperty, null);

}

else

{

property.SetValue(clone, property.GetValue(prototype, null), null);

}

}

}

}

public abstract class Weapon

{

}

public class LaserDisruptor: Weapon

{

public float RateOfFire

{

get

{

return rateOfFire;

}

set

{

rateOfFire = value;

}

}

private float rateOfFire = 1000;

}

public abstract class Entity {}

public class SpaceMonkey: Entity

{

public float Speed

{

get

{

return speed;

}

set

{

speed = value;

}

}

private float speed = 5;

private Weapon deepCopyWeapon = new LaserDisruptor();

[DeepCopyAttribute()]

public Weapon DeepCopyWeapon

{

get

{

return deepCopyWeapon;

}

set

{

deepCopyWeapon = value;

}

}

private Weapon shallowCopyWeapon = new LaserDisruptor();

public Weapon ShallowCopyWeapon

{

get

{

return shallowCopyWeapon;

}

set

{

shallowCopyWeapon = value;

}

}

}

[TestFixture]

public class PrototypeClonerTests

{

[Test]

public void Clone()

{

SpaceMonkey prototypeObject = new SpaceMonkey();

PrototypeCloner cloner = new PrototypeCloner();

Entity clonedObject = null;

clonedObject = (Entity)cloner.Clone(prototypeObject);

SpaceMonkey clonedMonkey = (SpaceMonkey)clonedObject;

// Testing for Equality

Assert.AreEqual( prototypeObject.Speed, clonedMonkey.Speed, “Fail1” );

// — This is false because it is a Deep Copy property and this class

// doesn’t override the Equals or == operator so the Assert’s

// AreSame and AreEqual tests have the identical result

Assert.IsFalse(

prototypeObject.DeepCopyWeapon == clonedMonkey.DeepCopyWeapon,

“Fail2” );

Assert.AreEqual(

((LaserDisruptor)prototypeObject.DeepCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.DeepCopyWeapon).RateOfFire, “Fail3” );

// — Are equal, as in the same object because it is the Shallow Copy

Assert.AreEqual( prototypeObject.ShallowCopyWeapon,

clonedMonkey.ShallowCopyWeapon,

“Fail4” );

Assert.AreEqual(

((LaserDisruptor)prototypeObject.ShallowCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.ShallowCopyWeapon).RateOfFire, “Fail5” );

// Testing for Same

// — Only the DeepCopyAttribute properties should be the same

// — Not same because it is not a referance object (float)

Assert.IsFalse(

object.ReferenceEquals( prototypeObject.Speed, clonedMonkey.Speed ),

“Fail6” );

// — Not same because it is a DeepCopyAttribute

Assert.IsFalse(

object.ReferenceEquals( prototypeObject.DeepCopyWeapon,

clonedMonkey.DeepCopyWeapon),

“Fail7” );

// — Not same because it is not a referance object (float)

Assert.IsFalse( object.ReferenceEquals(

((LaserDisruptor)prototypeObject.DeepCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.DeepCopyWeapon).RateOfFire), “Fail8” );

// — The only referance object that isn’t a deep copy Are Same

Assert.AreSame( prototypeObject.ShallowCopyWeapon,

clonedMonkey.ShallowCopyWeapon,

“Fail9” );

// — Not same because it is not a referance object (float)

Assert.IsFalse( object.ReferenceEquals(

((LaserDisruptor)prototypeObject.ShallowCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.ShallowCopyWeapon).RateOfFire),

“Fail10” );

}

}

Tags:

Comments are closed.