From 48e1674912ce5011ad60281d05f62369b2ea00d7 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Wed, 8 Feb 2012 18:47:45 +0000 Subject: [PATCH] Property-based shallow clones of value types are now also working correctly in the expression tree cloner git-svn-id: file:///srv/devel/repo-conversion/nusu@247 d2e56fa2-650e-0410-a79f-9358c0239efd --- .../ExpressionTreeCloner.PropertyBased.cs | 194 +++++++++++------- 1 file changed, 122 insertions(+), 72 deletions(-) diff --git a/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs b/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs index ab5873d..52cac35 100644 --- a/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs +++ b/Source/Cloning/ExpressionTreeCloner.PropertyBased.cs @@ -122,88 +122,138 @@ namespace Nuclex.Support.Cloning { return Expression.Lambda>(resultExpression, original).Compile(); } - /// 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 - /// - /// - /// The 'null' check is supposed to take place before running the cloner. This - /// avoids having redundant 'null' checks on nested types - first before calling - /// GetType() on the property to be cloned and second when runner the matching - /// cloner for the property. - /// - /// - /// This design also enables the cloning of nested value types (which can never - /// be null) without any null check whatsoever. - /// - /// - private static Func createShallowPropertyBasedCloner(Type clonedType) { - ParameterExpression original = Expression.Parameter(typeof(object), "original"); + /// 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 + /// + /// + /// The 'null' check is supposed to take place before running the cloner. This + /// avoids having redundant 'null' checks on nested types - first before calling + /// GetType() on the property to be cloned and second when runner the matching + /// cloner for the property. + /// + /// + /// This design also enables the cloning of nested value types (which can never + /// be null) without any null check whatsoever. + /// + /// + private static Func createShallowPropertyBasedCloner(Type clonedType) { + ParameterExpression original = Expression.Parameter(typeof(object), "original"); - var transferExpressions = new List(); - var variables = new List(); + var transferExpressions = new List(); + var variables = new List(); - if(clonedType.IsPrimitive || (clonedType == typeof(string))) { - // Primitives and strings are copied on direct assignment - transferExpressions.Add(original); - } else if(clonedType.IsArray) { - transferExpressions.Add( - generatePropertyBasedPrimitiveArrayTransferExpressions( - clonedType, original, variables, transferExpressions - ) - ); - } else { - // We need a variable to hold the clone because due to the assignments it - // won't be last in the block when we're finished - ParameterExpression clone = Expression.Variable(clonedType); - variables.Add(clone); + if(clonedType.IsPrimitive || (clonedType == typeof(string))) { + // Primitives and strings are copied on direct assignment + transferExpressions.Add(original); + } else if(clonedType.IsArray) { + transferExpressions.Add( + generatePropertyBasedPrimitiveArrayTransferExpressions( + clonedType, original, variables, transferExpressions + ) + ); + } else { + // We need a variable to hold the clone because due to the assignments it + // won't be last in the block when we're finished + ParameterExpression clone = Expression.Variable(clonedType); + variables.Add(clone); + transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType))); - // Give it a new instance of the type being cloned - transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType))); + // To access the properties of the original type, we need it to be of the actual + // type instead of an object, so perform a downcast + ParameterExpression typedOriginal = Expression.Variable(clonedType); + variables.Add(typedOriginal); + transferExpressions.Add( + Expression.Assign(typedOriginal, Expression.Convert(original, clonedType)) + ); - // To access the properties of the original type, we need it to be of the actual - // type instead of an object, so perform a downcast - ParameterExpression typedOriginal = Expression.Variable(clonedType); - variables.Add(typedOriginal); - transferExpressions.Add( - Expression.Assign(typedOriginal, Expression.Convert(original, clonedType)) - ); + generateShallowPropertyBasedComplexCloneExpressions( + clonedType, typedOriginal, clone, transferExpressions, variables + ); - // Enumerate all of the type's properties and generate transfer expressions for each - PropertyInfo[] propertyInfos = clonedType.GetProperties( - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.Instance | BindingFlags.FlattenHierarchy - ); - for(int index = 0; index < propertyInfos.Length; ++index) { - PropertyInfo propertyInfo = propertyInfos[index]; + // Make sure the clone is the last thing in the block to set the return value + transferExpressions.Add(clone); + } - transferExpressions.Add( - Expression.Assign( - Expression.Property(clone, propertyInfo), - Expression.Property(typedOriginal, propertyInfo) - ) - ); - } + // Turn all transfer expressions into a single block if necessary + Expression resultExpression; + if((transferExpressions.Count == 1) && (variables.Count == 0)) { + resultExpression = transferExpressions[0]; + } else { + resultExpression = Expression.Block(variables, transferExpressions); + } - // Make sure the clone is the last thing in the block to set the return value - transferExpressions.Add(clone); - } + // Value types require manual boxing + if(clonedType.IsValueType) { + resultExpression = Expression.Convert(resultExpression, typeof(object)); + } - // Turn all transfer expressions into a single block if necessary - Expression resultExpression; - if((transferExpressions.Count == 1) && (variables.Count == 0)) { - resultExpression = transferExpressions[0]; - } else { - resultExpression = Expression.Block(variables, transferExpressions); - } + return Expression.Lambda>(resultExpression, original).Compile(); + } - // Value types require manual boxing - if(clonedType.IsValueType) { - resultExpression = Expression.Convert(resultExpression, typeof(object)); - } + /// + /// Generates expressions to transfer the properties of a complex value type + /// + /// Complex value type that will be cloned + /// Original instance whose properties will be cloned + /// Target instance into which the properties will be copied + /// Receives the value transfer expressions + /// Receives temporary variables used during the clone + private static void generateShallowPropertyBasedComplexCloneExpressions( + Type clonedType, + ParameterExpression original, + ParameterExpression clone, + ICollection transferExpressions, + ICollection variables + ) { + // Enumerate all of the type's properties and generate transfer expressions for each + PropertyInfo[] propertyInfos = clonedType.GetProperties( + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.FlattenHierarchy + ); + for(int index = 0; index < propertyInfos.Length; ++index) { + PropertyInfo propertyInfo = propertyInfos[index]; + Type propertyType = propertyInfo.PropertyType; - return Expression.Lambda>(resultExpression, original).Compile(); - } + if(propertyType.IsPrimitive || (propertyType == typeof(string))) { + transferExpressions.Add( + Expression.Assign( + Expression.Property(clone, propertyInfo), + Expression.Property(original, propertyInfo) + ) + ); + } else if(propertyType.IsValueType) { + ParameterExpression originalProperty = Expression.Variable(propertyType); + variables.Add(originalProperty); + transferExpressions.Add( + Expression.Assign( + originalProperty, Expression.Property(original, propertyInfo) + ) + ); + + ParameterExpression clonedProperty = Expression.Variable(propertyType); + variables.Add(clonedProperty); + transferExpressions.Add( + Expression.Assign(clonedProperty, Expression.New(propertyType)) + ); + + generateShallowPropertyBasedComplexCloneExpressions(propertyType, originalProperty, clonedProperty, transferExpressions, variables); + + transferExpressions.Add( + Expression.Assign( + Expression.Property(clone, propertyInfo), clonedProperty + ) + ); + } else { + transferExpressions.Add( + Expression.Assign( + Expression.Property(clone, propertyInfo), + Expression.Property(original, propertyInfo) + ) + ); + } + } + } /// /// Generates state transfer expressions to copy an array of primitive types