Expression tree cloner now is able to clone complex types (non-recursively, at the moment)

git-svn-id: file:///srv/devel/repo-conversion/nusu@230 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-02-03 18:06:46 +00:00
parent 776a0749fd
commit 4743b5d056
2 changed files with 172 additions and 172 deletions

View File

@ -27,33 +27,33 @@ using NUnit.Framework;
namespace Nuclex.Support.Cloning { namespace Nuclex.Support.Cloning {
/// <summary>Unit Test for the expression tree-based cloner</summary> /// <summary>Unit Test for the expression tree-based cloner</summary>
[TestFixture] [TestFixture]
public class ExpressionTreeClonerTest : CloneFactoryTest { public class ExpressionTreeClonerTest : CloneFactoryTest {
/// <summary>Initializes a new unit test suite for the reflection cloner</summary> /// <summary>Initializes a new unit test suite for the reflection cloner</summary>
public ExpressionTreeClonerTest() { public ExpressionTreeClonerTest() {
this.cloneFactory = new ExpressionTreeCloner(); this.cloneFactory = new ExpressionTreeCloner();
} }
/// <summary>Verifies that clones of primitive types can be created</summary> /// <summary>Verifies that clones of primitive types can be created</summary>
[Test] [Test]
public void PrimitiveTypesCanBeCloned() { public void PrimitiveTypesCanBeCloned() {
int original = 12345; int original = 12345;
int clone = this.cloneFactory.DeepClone(original, false); int clone = this.cloneFactory.DeepClone(original, false);
Assert.AreEqual(original, clone); Assert.AreEqual(original, clone);
} }
/// <summary>Verifies that shallow clones of arrays can be made</summary> /// <summary>Verifies that shallow clones of arrays can be made</summary>
[Test] [Test]
public void ReferenceTypesCanBeCloned() { public void ReferenceTypesCanBeCloned() {
var original = new TestReferenceType() { TestField = 123, TestProperty = 456 }; var original = new TestReferenceType() { TestField = 123, TestProperty = 456 };
TestReferenceType clone = this.cloneFactory.DeepClone(original, false); TestReferenceType clone = this.cloneFactory.DeepClone(original, false);
Assert.AreNotSame(original, clone); Assert.AreNotSame(original, clone);
//Assert.AreEqual(original.TestField, clone.TestField); Assert.AreEqual(original.TestField, clone.TestField);
//Assert.AreEqual(original.TestProperty, clone.TestProperty); Assert.AreEqual(original.TestProperty, clone.TestProperty);
} }
#if false #if false
/// <summary>Verifies that shallow clones of arrays can be made</summary> /// <summary>Verifies that shallow clones of arrays can be made</summary>
@ -180,10 +180,10 @@ namespace Nuclex.Support.Cloning {
} }
#endif #endif
/// <summary>Clone factory being tested</summary> /// <summary>Clone factory being tested</summary>
private ICloneFactory cloneFactory; private ICloneFactory cloneFactory;
} }
} // namespace Nuclex.Support.Cloning } // namespace Nuclex.Support.Cloning

View File

@ -28,162 +28,162 @@ using System.Collections.Generic;
namespace Nuclex.Support.Cloning { namespace Nuclex.Support.Cloning {
/// <summary>An action that takes its arguments as references to a structure</summary> /// <summary>An action that takes its arguments as references to a structure</summary>
/// <typeparam name="TFirst">Type of the first argument to the method</typeparam> /// <typeparam name="TFirst">Type of the first argument to the method</typeparam>
/// <typeparam name="TSecond">Type of the second argument to the method</typeparam> /// <typeparam name="TSecond">Type of the second argument to the method</typeparam>
/// <param name="first">First argument to the method</param> /// <param name="first">First argument to the method</param>
/// <param name="second">Second argument to the method</param> /// <param name="second">Second argument to the method</param>
public delegate void ReferenceAction<TFirst, TSecond>(ref TFirst first, ref TSecond second) public delegate void ReferenceAction<TFirst, TSecond>(ref TFirst first, ref TSecond second)
where TFirst : struct where TFirst : struct
where TSecond : struct; where TSecond : struct;
/// <summary> /// <summary>
/// Cloning factory which uses expression trees to improve performance when cloning /// Cloning factory which uses expression trees to improve performance when cloning
/// is a high-frequency action. /// is a high-frequency action.
/// </summary> /// </summary>
public class ExpressionTreeCloner : ICloneFactory { public class ExpressionTreeCloner : ICloneFactory {
/// <summary>Initializes the static members of the expression tree cloner</summary> /// <summary>Initializes the static members of the expression tree cloner</summary>
static ExpressionTreeCloner() { static ExpressionTreeCloner() {
shallowCloners = new ConcurrentDictionary<Type, Func<object, object>>(); shallowCloners = new ConcurrentDictionary<Type, Delegate>();
deepCloners = new ConcurrentDictionary<Type, Func<object, object>>(); deepCloners = new ConcurrentDictionary<Type, Delegate>();
} }
/// <summary> /// <summary>
/// Creates a deep clone of the specified object, also creating clones of all /// Creates a deep clone of the specified object, also creating clones of all
/// child objects being referenced /// child objects being referenced
/// </summary> /// </summary>
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam> /// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
/// <param name="objectToClone">Object that will be cloned</param> /// <param name="objectToClone">Object that will be cloned</param>
/// <param name="usePropertyBasedClone"> /// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A deep clone of the provided object</returns> /// <returns>A deep clone of the provided object</returns>
public static TCloned DeepClone<TCloned>( public static TCloned DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone TCloned objectToClone, bool usePropertyBasedClone
) { ) {
if(usePropertyBasedClone) { if(usePropertyBasedClone) {
throw new NotImplementedException("Not implemented yet"); throw new NotImplementedException("Not implemented yet");
} else { } else {
Func<object, object> cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned)); Func<TCloned, TCloned> cloner = getOrCreateDeepFieldBasedCloner<TCloned>();
return (TCloned)cloner(objectToClone); return cloner(objectToClone);
} }
} }
/// <summary> /// <summary>
/// Creates a shallow clone of the specified object, reusing any referenced objects /// Creates a shallow clone of the specified object, reusing any referenced objects
/// </summary> /// </summary>
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam> /// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
/// <param name="objectToClone">Object that will be cloned</param> /// <param name="objectToClone">Object that will be cloned</param>
/// <param name="usePropertyBasedClone"> /// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A shallow clone of the provided object</returns> /// <returns>A shallow clone of the provided object</returns>
public static TCloned ShallowClone<TCloned>( public static TCloned ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone TCloned objectToClone, bool usePropertyBasedClone
) { ) {
throw new NotImplementedException("Not implemented yet"); throw new NotImplementedException("Not implemented yet");
} }
/// <summary> /// <summary>
/// Creates a deep clone of the specified object, also creating clones of all /// Creates a deep clone of the specified object, also creating clones of all
/// child objects being referenced /// child objects being referenced
/// </summary> /// </summary>
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam> /// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
/// <param name="objectToClone">Object that will be cloned</param> /// <param name="objectToClone">Object that will be cloned</param>
/// <param name="usePropertyBasedClone"> /// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A deep clone of the provided object</returns> /// <returns>A deep clone of the provided object</returns>
TCloned ICloneFactory.DeepClone<TCloned>( TCloned ICloneFactory.DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone TCloned objectToClone, bool usePropertyBasedClone
) { ) {
return ExpressionTreeCloner.DeepClone<TCloned>(objectToClone, usePropertyBasedClone); return ExpressionTreeCloner.DeepClone<TCloned>(objectToClone, usePropertyBasedClone);
} }
/// <summary> /// <summary>
/// Creates a shallow clone of the specified object, reusing any referenced objects /// Creates a shallow clone of the specified object, reusing any referenced objects
/// </summary> /// </summary>
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam> /// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
/// <param name="objectToClone">Object that will be cloned</param> /// <param name="objectToClone">Object that will be cloned</param>
/// <param name="usePropertyBasedClone"> /// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A shallow clone of the provided object</returns> /// <returns>A shallow clone of the provided object</returns>
TCloned ICloneFactory.ShallowClone<TCloned>( TCloned ICloneFactory.ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone TCloned objectToClone, bool usePropertyBasedClone
) { ) {
return ExpressionTreeCloner.ShallowClone<TCloned>(objectToClone, usePropertyBasedClone); return ExpressionTreeCloner.ShallowClone<TCloned>(objectToClone, usePropertyBasedClone);
} }
/// <summary> /// <summary>
/// Retrieves the existing clone method for the specified type or compiles one if /// Retrieves the existing clone method for the specified type or compiles one if
/// none exists for the type yet /// none exists for the type yet
/// </summary> /// </summary>
/// <param name="type">Type for which a clone method will be retrieved</param> /// <typeparam name="TCloned">Type for which a clone method will be retrieved</typeparam>
/// <returns>The clone method for the specified type</returns> /// <returns>The clone method for the specified type</returns>
private static Func<object, object> getOrCreateDeepFieldBasedCloner(Type type) { private static Func<TCloned, TCloned> getOrCreateDeepFieldBasedCloner<TCloned>() {
Func<object, object> cloner; Type clonedType = typeof(TCloned);
if(!deepCloners.TryGetValue(type, out cloner)) { Delegate clonerAsDelegate;
cloner = createDeepFieldBasedCloner(type); if(deepCloners.TryGetValue(clonedType, out clonerAsDelegate)) {
deepCloners.TryAdd(type, cloner); return (Func<TCloned, TCloned>)clonerAsDelegate;
} } else {
Func<TCloned, TCloned> cloner = createDeepFieldBasedCloner<TCloned>();
deepCloners.TryAdd(clonedType, cloner);
return cloner;
}
}
return cloner; /// <summary>Compiles a method that creates a clone of an object</summary>
} /// <param name="type">Type for which a clone method will be created</param>
/// <returns>A method that clones an object of the provided type</returns>
private static Func<TCloned, TCloned> createDeepFieldBasedCloner<TCloned>() {
Type clonedType = typeof(TCloned);
FieldInfo[] fieldInfos = clonedType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
/// <summary>Compiles a method that creates a clone of an object</summary> ParameterExpression original = Expression.Parameter(typeof(TCloned), "original");
/// <param name="type">Type for which a clone method will be created</param> ParameterExpression clone = Expression.Variable(typeof(TCloned), "clone");
/// <returns>A method that clones an object of the provided type</returns>
private static Func<object, object> createDeepFieldBasedCloner(Type type) {
FieldInfo[] fieldInfos = type.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
ParameterExpression original = Expression.Parameter(typeof(object), "original"); var transferExpressions = new List<Expression>();
ParameterExpression clone = Expression.Variable(typeof(object), "clone");
//ParameterExpression typedOriginal = Expression.Variable(type, "typedOriginal");
var transferExpressions = new List<Expression>(); if(clonedType.IsPrimitive || (clonedType == typeof(string))) {
transferExpressions.Add(Expression.Assign(clone, original));
} else if(clonedType.IsArray) {
throw new NotImplementedException("Not implemented yet");
} else {
transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType)));
if(type.IsPrimitive || (type == typeof(string))) { for(int index = 0; index < fieldInfos.Length; ++index) {
transferExpressions.Add(Expression.Assign(clone, original)); FieldInfo fieldInfo = fieldInfos[index];
} else if(type.IsArray) { Type fieldType = fieldInfo.FieldType;
//throw new NotImplementedException("Not implemented yet");
} else {
transferExpressions.Add(Expression.Assign(clone, Expression.New(type)));
/*
transferExpressions.Add(
Expression.Assign(typedOriginal, Expression.Convert(original, type))
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
Type fieldType = fieldInfo.FieldType;
if(fieldType.IsPrimitive) { if(fieldType.IsPrimitive) {
transferExpressions.Add( transferExpressions.Add(
Expression.Assign( Expression.Assign(
Expression.Field(clone, fieldInfo), Expression.Field(clone, fieldInfo),
Expression.Field(typedOriginal, fieldInfo) Expression.Field(original, fieldInfo)
) )
); );
} }
} }
*/
}
Expression<Func<object, object>> expression = transferExpressions.Add(clone);
Expression.Lambda<Func<object, object>>( }
Expression.Block(
new[] { clone },
transferExpressions
),
original
);
return expression.Compile(); Expression<Func<TCloned, TCloned>> expression =
} Expression.Lambda<Func<TCloned, TCloned>>(
Expression.Block(
new[] { clone },
transferExpressions
),
original
);
return expression.Compile();
}
#if false #if false
/// <summary> /// <summary>
@ -262,12 +262,12 @@ namespace Nuclex.Support.Cloning {
} }
#endif #endif
/// <summary>Compiled cloners that perform shallow clone operations</summary> /// <summary>Compiled cloners that perform shallow clone operations</summary>
private static ConcurrentDictionary<Type, Func<object, object>> shallowCloners; private static ConcurrentDictionary<Type, Delegate> shallowCloners;
/// <summary>Compiled cloners that perform deep clone operations</summary> /// <summary>Compiled cloners that perform deep clone operations</summary>
private static ConcurrentDictionary<Type, Func<object, object>> deepCloners; private static ConcurrentDictionary<Type, Delegate> deepCloners;
} }
} // namespace Nuclex.Support.Cloning } // namespace Nuclex.Support.Cloning