diff --git a/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs b/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs index 876ce47..a4caef2 100644 --- a/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs +++ b/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs @@ -29,8 +29,6 @@ namespace Nuclex.Support.Cloning { partial class ExpressionTreeCloner : ICloneFactory { -#if false - /// Compiles a method that creates a deep clone of an object /// Type for which a clone method will be created /// A method that clones an object of the provided type @@ -383,18 +381,40 @@ namespace Nuclex.Support.Cloning { ) ); } else if(propertyType.IsValueType) { + ParameterExpression originalProperty = Expression.Variable(propertyType); + variables.Add(originalProperty); + ParameterExpression clonedProperty = Expression.Variable(propertyType); + variables.Add(clonedProperty); + + transferExpressions.Add( + Expression.Assign( + originalProperty, Expression.Property(original, propertyInfo) + ) + ); + transferExpressions.Add( + Expression.Assign(clonedProperty, Expression.New(propertyType)) + ); + // A nested value type is part of the parent and will have its propertys directly // assigned without boxing, new instance creation or anything like that. generatePropertyBasedComplexTypeTransferExpressions( propertyType, - Expression.Property(original, propertyInfo), - Expression.Property(clone, propertyInfo), + originalProperty, + clonedProperty, variables, transferExpressions ); + + transferExpressions.Add( + Expression.Assign( + Expression.Property(clone, propertyInfo), + clonedProperty + ) + ); + } else { generatePropertyBasedReferenceTypeTransferExpressions( - original, clone, transferExpressions, propertyInfo, propertyType + original, clone, transferExpressions, variables, propertyInfo, propertyType ); } } @@ -414,9 +434,17 @@ namespace Nuclex.Support.Cloning { Expression original, Expression clone, ICollection transferExpressions, + ICollection variables, PropertyInfo propertyInfo, Type propertyType ) { + ParameterExpression originalProperty = Expression.Variable(propertyType); + variables.Add(originalProperty); + + transferExpressions.Add( + Expression.Assign(originalProperty, Expression.Property(original, propertyInfo)) + ); + // Reference types and arrays require special care because they can be null, // so gather the transfer expressions in a separate block for the null check var propertyTransferExpressions = new List(); @@ -431,7 +459,7 @@ namespace Nuclex.Support.Cloning { // For primitive arrays, the Array.Clone() method is sufficient propertyClone = generatePropertyBasedPrimitiveArrayTransferExpressions( propertyType, - Expression.Property(original, propertyInfo), + originalProperty, propertyVariables, propertyTransferExpressions ); @@ -439,7 +467,7 @@ namespace Nuclex.Support.Cloning { // Arrays of complex types require manual cloning propertyClone = generatePropertyBasedComplexArrayTransferExpressions( propertyType, - Expression.Property(original, propertyInfo), + originalProperty, propertyVariables, propertyTransferExpressions ); @@ -473,12 +501,10 @@ namespace Nuclex.Support.Cloning { Expression.Call( Expression.Call( getOrCreateClonerMethodInfo, - Expression.Call( - Expression.Property(original, propertyInfo), getTypeMethodInfo - ) + Expression.Call(originalProperty, getTypeMethodInfo) ), invokeMethodInfo, - Expression.Property(original, propertyInfo) + originalProperty ), propertyType ) @@ -491,15 +517,13 @@ namespace Nuclex.Support.Cloning { transferExpressions.Add( Expression.IfThen( Expression.NotEqual( - Expression.Property(original, propertyInfo), Expression.Constant(null) + originalProperty, Expression.Constant(null) ), Expression.Block(propertyVariables, propertyTransferExpressions) ) ); } -#endif - } } // namespace Nuclex.Support.Cloning diff --git a/Source/Cloning/ExpressionTreeCloner.Test.cs b/Source/Cloning/ExpressionTreeCloner.Test.cs index e0f4024..cfb2c67 100644 --- a/Source/Cloning/ExpressionTreeCloner.Test.cs +++ b/Source/Cloning/ExpressionTreeCloner.Test.cs @@ -172,7 +172,6 @@ namespace Nuclex.Support.Cloning { } #endif -#if false /// /// Verifies that a property-based deep clone of a value type can be performed /// @@ -192,7 +191,6 @@ 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; diff --git a/Source/Cloning/ExpressionTreeCloner.cs b/Source/Cloning/ExpressionTreeCloner.cs index 9e5cfc5..c53f9f5 100644 --- a/Source/Cloning/ExpressionTreeCloner.cs +++ b/Source/Cloning/ExpressionTreeCloner.cs @@ -35,6 +35,8 @@ namespace Nuclex.Support.Cloning { static ExpressionTreeCloner() { shallowFieldBasedCloners = new ConcurrentDictionary>(); deepFieldBasedCloners = new ConcurrentDictionary>(); + shallowPropertyBasedCloners = new ConcurrentDictionary>(); + deepPropertyBasedCloners = new ConcurrentDictionary>(); } /// @@ -55,12 +57,13 @@ namespace Nuclex.Support.Cloning { return default(TCloned); } + Func cloner; if(usePropertyBasedClone) { - throw new NotImplementedException("Not implemented yet"); + cloner = getOrCreateDeepPropertyBasedCloner(typeof(TCloned)); } else { - Func cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned)); - return (TCloned)cloner(objectToCloneAsObject); + cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned)); } + return (TCloned)cloner(objectToCloneAsObject); } /// @@ -164,17 +167,6 @@ namespace Nuclex.Support.Cloning { throw new NotImplementedException(); } - /// - /// Compiles a method that copies the state of one object into another object - /// - /// Type of object whose state will be copied - /// Whether to create clones of the referenced objects - /// A method that copies the state from one object into another object - public static Action CreateReferenceCopier(bool deepClone) - where TCloned : class { - throw new NotImplementedException(); - } - #endif /// @@ -211,6 +203,41 @@ namespace Nuclex.Support.Cloning { return cloner; } + /// + /// 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 getOrCreateShallowPropertyBasedCloner(Type clonedType) { + Func cloner; + + if(!shallowPropertyBasedCloners.TryGetValue(clonedType, out cloner)) { + throw new NotImplementedException(); + //cloner = createShallowPropertyBasedCloner(clonedType); + shallowPropertyBasedCloners.TryAdd(clonedType, cloner); + } + + return cloner; + } + + /// + /// 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 getOrCreateDeepPropertyBasedCloner(Type clonedType) { + Func cloner; + + if(!deepPropertyBasedCloners.TryGetValue(clonedType, out cloner)) { + cloner = createDeepPropertyBasedCloner(clonedType); + deepPropertyBasedCloners.TryAdd(clonedType, cloner); + } + + return cloner; + } + /// Compiled cloners that perform shallow clone operations private static ConcurrentDictionary> shallowFieldBasedCloners; /// Compiled cloners that perform deep clone operations diff --git a/Source/Cloning/ReflectionCloner.cs b/Source/Cloning/ReflectionCloner.cs index c7e7466..869f13d 100644 --- a/Source/Cloning/ReflectionCloner.cs +++ b/Source/Cloning/ReflectionCloner.cs @@ -120,7 +120,11 @@ namespace Nuclex.Support.Cloning { /// A clone of the original instance private static object shallowCloneComplexFieldBased(object original) { Type originalType = original.GetType(); - object clone = FormatterServices.GetUninitializedObject(originalType); +#if (XBOX360 || WINDOWS_PHONE) + object clone = Activator.CreateInstance(originalType); +#else + object clone = FormatterServices.GetUninitializedObject(originalType); +#endif FieldInfo[] fieldInfos = originalType.GetFields( BindingFlags.Public | BindingFlags.NonPublic | @@ -197,7 +201,11 @@ namespace Nuclex.Support.Cloning { /// A clone of the original instance private static object deepCloneComplexFieldBased(object original) { Type originalType = original.GetType(); - object clone = FormatterServices.GetUninitializedObject(originalType); +#if (XBOX360 || WINDOWS_PHONE) + object clone = Activator.CreateInstance(originalType); +#else + object clone = FormatterServices.GetUninitializedObject(originalType); +#endif FieldInfo[] fieldInfos = originalType.GetFields( BindingFlags.Public | BindingFlags.NonPublic |