diff --git a/Source/Cloning/ExpressionTreeCloner.Test.cs b/Source/Cloning/ExpressionTreeCloner.Test.cs index 618338a..f8461b7 100644 --- a/Source/Cloning/ExpressionTreeCloner.Test.cs +++ b/Source/Cloning/ExpressionTreeCloner.Test.cs @@ -27,23 +27,35 @@ using NUnit.Framework; namespace Nuclex.Support.Cloning { - /// Unit Test for the expression tree-based cloner - [TestFixture] - public class ExpressionTreeClonerTest : CloneFactoryTest { + /// Unit Test for the expression tree-based cloner + [TestFixture] + public class ExpressionTreeClonerTest : CloneFactoryTest { - /// Initializes a new unit test suite for the reflection cloner - public ExpressionTreeClonerTest() { - this.cloneFactory = new ExpressionTreeCloner(); - } + /// Initializes a new unit test suite for the reflection cloner + public ExpressionTreeClonerTest() { + this.cloneFactory = new ExpressionTreeCloner(); + } - /// Verifies that clones of primitive types can be created - [Test] - public void PrimitiveTypesCanBeCloned() { - int original = 12345; - int clone = this.cloneFactory.ShallowClone(original, false); - Assert.AreEqual(original, clone); - } + /// Verifies that clones of primitive types can be created + [Test] + public void PrimitiveTypesCanBeCloned() { + int original = 12345; + int clone = this.cloneFactory.DeepClone(original, false); + Assert.AreEqual(original, clone); + } + /// Verifies that shallow clones of arrays can be made + [Test] + public void ReferenceTypesCanBeCloned() { + var original = new TestReferenceType() { TestField = 123, TestProperty = 456 }; + TestReferenceType clone = this.cloneFactory.DeepClone(original, false); + + Assert.AreNotSame(original, clone); + //Assert.AreEqual(original.TestField, clone.TestField); + //Assert.AreEqual(original.TestProperty, clone.TestProperty); + } + +#if false /// Verifies that shallow clones of arrays can be made [Test] public void ShallowClonesOfArraysCanBeMade() { @@ -166,11 +178,12 @@ namespace Nuclex.Support.Cloning { HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, true); VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true); } +#endif - /// Clone factory being tested - private ICloneFactory cloneFactory; + /// Clone factory being tested + private ICloneFactory cloneFactory; - } + } } // namespace Nuclex.Support.Cloning diff --git a/Source/Cloning/ExpressionTreeCloner.cs b/Source/Cloning/ExpressionTreeCloner.cs index cedc9af..1e0d38a 100644 --- a/Source/Cloning/ExpressionTreeCloner.cs +++ b/Source/Cloning/ExpressionTreeCloner.cs @@ -22,91 +22,168 @@ License along with this library using System; using System.Collections.Concurrent; +using System.Reflection; +using System.Linq.Expressions; +using System.Collections.Generic; namespace Nuclex.Support.Cloning { - /// An action that takes its arguments as references to a structure - /// Type of the first argument to the method - /// Type of the second argument to the method - /// First argument to the method - /// Second argument to the method - public delegate void ReferenceAction(ref TFirst first, ref TSecond second) - where TFirst : struct - where TSecond : struct; + /// An action that takes its arguments as references to a structure + /// Type of the first argument to the method + /// Type of the second argument to the method + /// First argument to the method + /// Second argument to the method + public delegate void ReferenceAction(ref TFirst first, ref TSecond second) + where TFirst : struct + where TSecond : struct; - /// - /// Cloning factory which uses expression trees to improve performance when cloning - /// is a high-frequency action. - /// - public class ExpressionTreeCloner : ICloneFactory { + /// + /// Cloning factory which uses expression trees to improve performance when cloning + /// is a high-frequency action. + /// + public class ExpressionTreeCloner : ICloneFactory { - /// Initializes the static members of the expression tree cloner - static ExpressionTreeCloner() { - shallowCloners = new ConcurrentDictionary>(); - deepCloners = new ConcurrentDictionary>(); - } + /// Initializes the static members of the expression tree cloner + static ExpressionTreeCloner() { + shallowCloners = new ConcurrentDictionary>(); + deepCloners = new ConcurrentDictionary>(); + } - /// - /// Creates a deep clone of the specified object, also creating clones of all - /// child objects being referenced - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A deep clone of the provided object - public static TCloned DeepClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - throw new NotImplementedException("Not implemented yet"); - } + /// + /// Creates a deep clone of the specified object, also creating clones of all + /// child objects being referenced + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A deep clone of the provided object + public static TCloned DeepClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + if(usePropertyBasedClone) { + throw new NotImplementedException("Not implemented yet"); + } else { + Func cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned)); + return (TCloned)cloner(objectToClone); + } + } - /// - /// Creates a shallow clone of the specified object, reusing any referenced objects - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A shallow clone of the provided object - public static TCloned ShallowClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - throw new NotImplementedException("Not implemented yet"); - } + /// + /// Creates a shallow clone of the specified object, reusing any referenced objects + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A shallow clone of the provided object + public static TCloned ShallowClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + throw new NotImplementedException("Not implemented yet"); + } - /// - /// Creates a deep clone of the specified object, also creating clones of all - /// child objects being referenced - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A deep clone of the provided object - TCloned ICloneFactory.DeepClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - return ExpressionTreeCloner.DeepClone(objectToClone, usePropertyBasedClone); - } + /// + /// Creates a deep clone of the specified object, also creating clones of all + /// child objects being referenced + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A deep clone of the provided object + TCloned ICloneFactory.DeepClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + return ExpressionTreeCloner.DeepClone(objectToClone, usePropertyBasedClone); + } - /// - /// Creates a shallow clone of the specified object, reusing any referenced objects - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A shallow clone of the provided object - TCloned ICloneFactory.ShallowClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - return ExpressionTreeCloner.ShallowClone(objectToClone, usePropertyBasedClone); - } + /// + /// Creates a shallow clone of the specified object, reusing any referenced objects + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A shallow clone of the provided object + TCloned ICloneFactory.ShallowClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + return ExpressionTreeCloner.ShallowClone(objectToClone, usePropertyBasedClone); + } + + /// + /// Retrieves the existing clone method for the specified type or compiles one if + /// none exists for the type yet + /// + /// Type for which a clone method will be retrieved + /// The clone method for the specified type + private static Func getOrCreateDeepFieldBasedCloner(Type type) { + Func cloner; + if(!deepCloners.TryGetValue(type, out cloner)) { + cloner = createDeepFieldBasedCloner(type); + deepCloners.TryAdd(type, cloner); + } + + return cloner; + } + + /// Compiles a method that creates a clone of an object + /// Type for which a clone method will be created + /// A method that clones an object of the provided type + private static Func createDeepFieldBasedCloner(Type type) { + FieldInfo[] fieldInfos = type.GetFields( + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.FlattenHierarchy + ); + + ParameterExpression original = Expression.Parameter(typeof(object), "original"); + ParameterExpression clone = Expression.Variable(typeof(object), "clone"); + //ParameterExpression typedOriginal = Expression.Variable(type, "typedOriginal"); + + var transferExpressions = new List(); + + if(type.IsPrimitive || (type == typeof(string))) { + transferExpressions.Add(Expression.Assign(clone, original)); + } else if(type.IsArray) { + //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) { + transferExpressions.Add( + Expression.Assign( + Expression.Field(clone, fieldInfo), + Expression.Field(typedOriginal, fieldInfo) + ) + ); + } + } + */ + } + + Expression> expression = + Expression.Lambda>( + Expression.Block( + new[] { clone }, + transferExpressions + ), + original + ); + + return expression.Compile(); + } #if false /// @@ -185,12 +262,12 @@ namespace Nuclex.Support.Cloning { } #endif - /// Compiled cloners that perform shallow clone operations - private static ConcurrentDictionary> shallowCloners; - /// Compiled cloners that perform deep clone operations - private static ConcurrentDictionary> deepCloners; + /// Compiled cloners that perform shallow clone operations + private static ConcurrentDictionary> shallowCloners; + /// Compiled cloners that perform deep clone operations + private static ConcurrentDictionary> deepCloners; - } + } } // namespace Nuclex.Support.Cloning