예제 #1
0
        /// <summary>Generates state transfer expressions to copy a complex type</summary>
        /// <param name="clonedType">Complex type that will be cloned</param>
        /// <param name="original">Variable expression for the original instance</param>
        /// <param name="clone">Variable expression for the cloned instance</param>
        /// <param name="variables">Receives variables used by the transfer expressions</param>
        /// <param name="transferExpressions">Receives the generated transfer expressions</param>
        private static void generateFieldBasedComplexTypeTransferExpressions(
            Type clonedType,     // Actual, concrete type (not declared type)
            Expression original, // Expected to be an object
            Expression clone,    // As actual, concrete type
            IList <ParameterExpression> variables,
            ICollection <Expression> transferExpressions
            )
        {
            // Enumerate all of the type's fields and generate transfer expressions for each
            var fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
                clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
                );

            for (var index = 0; index < fieldInfos.Length; ++index)
            {
                var fieldInfo = fieldInfos[index];
                var fieldType = fieldInfo.FieldType;

                if (fieldType.IsPrimitive || (fieldType == typeof(string)))
                {
                    // Primitive types and strings can be transferred by simple assignment
                    transferExpressions.Add(
                        Expression.Assign(
                            Expression.Field(clone, fieldInfo),
                            Expression.Field(original, fieldInfo)
                            )
                        );
                }
                else if (fieldType.IsValueType)
                {
                    // A nested value type is part of the parent and will have its fields directly
                    // assigned without boxing, new instance creation or anything like that.
                    generateFieldBasedComplexTypeTransferExpressions(
                        fieldType,
                        Expression.Field(original, fieldInfo),
                        Expression.Field(clone, fieldInfo),
                        variables,
                        transferExpressions
                        );
                }
                else
                {
                    generateFieldBasedReferenceTypeTransferExpressions(
                        original, clone, transferExpressions, fieldInfo, fieldType
                        );
                }
            }
        }
예제 #2
0
        /// <summary>Compiles a method that creates a shallow clone of an object</summary>
        /// <param name="clonedType">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 <object, object> createShallowFieldBasedCloner(Type clonedType)
        {
            var original = Expression.Parameter(typeof(object), "original");

            var transferExpressions = new List <Expression>();
            var variables           = new List <ParameterExpression>();

            if (clonedType.IsPrimitive || clonedType.IsValueType || (clonedType == typeof(string)))
            {
                // Primitives and strings are copied on direct assignment
                transferExpressions.Add(original);
            }
            else if (clonedType.IsArray)
            {
                transferExpressions.Add(
                    generateFieldBasedPrimitiveArrayTransferExpressions(
                        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
                var clone = Expression.Variable(clonedType);
                variables.Add(clone);

                // To access the fields of the original type, we need it to be of the actual
                // type instead of an object, so perform a downcast
                var typedOriginal = Expression.Variable(clonedType);
                variables.Add(typedOriginal);
                transferExpressions.Add(
                    Expression.Assign(typedOriginal, Expression.Convert(original, clonedType))
                    );

                // Give it a new instance of the type being cloned
                var getUninitializedObjectMethodInfo = typeof(FormatterServices).GetMethod(
                    "GetUninitializedObject", BindingFlags.Static | BindingFlags.Public
                    );
                transferExpressions.Add(
                    Expression.Assign(
                        clone,
                        Expression.Convert(
                            Expression.Call(
                                getUninitializedObjectMethodInfo, Expression.Constant(clonedType)
                                ),
                            clonedType
                            )
                        )
                    );

                // Enumerate all of the type's fields and generate transfer expressions for each
                var fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
                    clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
                    );
                transferExpressions.AddRange(
                    fieldInfos.Select(fieldInfo => Expression.Assign(Expression.Field(clone, fieldInfo), Expression.Field(typedOriginal, fieldInfo))));

                // Make sure the clone is the last thing in the block to set the return value
                transferExpressions.Add(clone);
            }

            // 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);
            }

            // Value types require manual boxing
            if (clonedType.IsValueType)
            {
                resultExpression = Expression.Convert(resultExpression, typeof(object));
            }

            return(Expression.Lambda <Func <object, object> >(resultExpression, original).Compile());
        }