Exemple #1
0
        /// <summary>
        /// Generates state transfer expressions to copy a complex type
        /// </summary>
        /// <param name="complexType">Complex type that will be cloned</param>
        /// <param name="source">Variable expression for the original instance</param>
        /// <param name="target">Variable expression for the cloned instance</param>
        /// <param name="expression">Receives the generated transfer expressions</param>
        void GenerateFieldBasedComplexTypeTransferExpressions(Type complexType, Expression source, Expression target, ICollection <Expression> expression)
        {
            // Enumerate all of the type's fields and generate transfer expressions for each
            FieldInfo[] fieldInfos = GetFieldInfosIncludingBaseClasses(complexType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                Type fieldType = fieldInfo.FieldType;

                if (TypeIsPrimitiveOrString(fieldType))
                {
                    expression.Add(CloneExpressionHelper.CreateCopyFieldExpression(source, target, 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(source, fieldInfo), Expression.Field(target, fieldInfo), expression);
                }
                else
                {
                    GenerateFieldBasedReferenceTypeTransferExpressions(source, target, expression, fieldInfo);
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Generates the expressions to transfer a reference type (array or class)
        /// </summary>
        /// <param name="original">Original value that will be cloned</param>
        /// <param name="clone">Variable that will receive the cloned value</param>
        /// <param name="expressions">
        /// Receives the expression generated to transfer the values
        /// </param>
        /// <param name="fieldInfo">Reflection informations about the field being cloned</param>
        void GenerateFieldBasedReferenceTypeTransferExpressions(Expression original, Expression clone, ICollection <Expression> expressions, FieldInfo fieldInfo)
        {
            // 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
            List <Expression>          fieldExpressions = new List <Expression>();
            List <ParameterExpression> fieldVariables   = new List <ParameterExpression>();

            Type fieldType = fieldInfo.FieldType;

            if (fieldType.IsArray)
            {
                Expression fieldClone = GenerateFieldBasedComplexArrayTransferExpressions(fieldType, fieldType.GetElementType(), Expression.Field(original, fieldInfo), fieldVariables, fieldExpressions);
                fieldExpressions.Add(CloneExpressionHelper.CreateSetFieldExpression(clone, fieldClone, fieldInfo));
            }
            else
            {
                fieldExpressions.Add(CloneExpressionHelper.CreateCopyComplexFieldExpression(original, clone, fieldInfo, _objectDictionary));
            }

            expressions.Add(
                Expression.IfThen(
                    Expression.NotEqual(Expression.Field(original, fieldInfo), Expression.Constant(null)),
                    Expression.Block(fieldVariables, fieldExpressions))
                );
        }
Exemple #3
0
        /// <summary>
        /// Generates state transfer expressions to copy an array of complex types
        /// </summary>
        /// <param name="arrayType">Type of array that will be cloned</param>
        /// <param name="elementType">Type of the elements of the array</param>
        /// <param name="originalArray">Variable expression for the original array</param>
        /// <param name="arrayVariables">Receives variables used by the transfer expressions</param>
        /// <param name="arrayExpressions">Receives the generated transfer expressions</param>
        /// <returns>The variable holding the cloned array</returns>
        ParameterExpression GenerateFieldBasedComplexArrayTransferExpressions(Type arrayType, Type elementType, Expression originalArray, ICollection <ParameterExpression> arrayVariables, ICollection <Expression> arrayExpressions)
        {
            // We need a temporary variable in order to transfer the elements of the array
            ParameterExpression arrayClone = Expression.Variable(arrayType);

            arrayVariables.Add(arrayClone);

            int dimensionCount = arrayType.GetArrayRank();

            var lengths = new List <ParameterExpression>();
            var indexes = new List <ParameterExpression>();
            var labels  = new List <LabelTarget>();

            // Retrieve the length of each of the array's dimensions
            for (int index = 0; index < dimensionCount; ++index)
            {
                // Obtain the length of the array in the current dimension
                lengths.Add(Expression.Variable(typeof(int)));
                arrayVariables.Add(lengths[index]);
                arrayExpressions.Add(Expression.Assign(lengths[index], Expression.Call(originalArray, _arrayGetLengthMethodInfo, Expression.Constant(index))));

                // Set up a variable to index the array in this dimension
                indexes.Add(Expression.Variable(typeof(int)));
                arrayVariables.Add(indexes[index]);

                // Also set up a label than can be used to break out of the dimension's transfer loop
                labels.Add(Expression.Label());
            }

            // Create a new (empty) array with the same dimensions and lengths as the original
            arrayExpressions.Add(Expression.Assign(arrayClone, Expression.NewArrayBounds(elementType, lengths)));

            // Initialize the indexer of the outer loop (indexers are initialized one up
            // in the loops (ie. before the loop using it begins), so we have to set this
            // one outside of the loop building code.
            arrayExpressions.Add(Expression.Assign(indexes[0], Expression.Constant(0)));

            // Build the nested loops (one for each dimension) from the inside out
            Expression innerLoop = null;

            for (int index = dimensionCount - 1; index >= 0; --index)
            {
                var loopVariables   = new List <ParameterExpression>();
                var loopExpressions = new List <Expression> {
                    Expression.IfThen(Expression.GreaterThanOrEqual(indexes[index], lengths[index]), Expression.Break(labels[index]))
                };

                // If we reached the end of the current array dimension, break the loop

                if (innerLoop == null) // The innermost loop clones an actual array element
                {
                    if (TypeIsPrimitiveOrString(elementType))
                    {
                        loopExpressions.Add(Expression.Assign(Expression.ArrayAccess(arrayClone, indexes), Expression.ArrayAccess(originalArray, indexes)));
                    }
                    else if (elementType.IsValueType)
                    {
                        GenerateFieldBasedComplexTypeTransferExpressions(elementType, Expression.ArrayAccess(originalArray, indexes), Expression.ArrayAccess(arrayClone, indexes), loopExpressions);
                    }
                    else
                    {
                        var nestedVariables   = new List <ParameterExpression>();
                        var nestedExpressions = new List <Expression>();

                        // A nested array should be cloned by directly creating a new array (not invoking a cloner) since you cannot derive from an array
                        if (elementType.IsArray)
                        {
                            Type       nestedElementType = elementType.GetElementType();
                            Expression clonedElement     = TypeIsPrimitiveOrString(nestedElementType)
                                ? GenerateFieldBasedPrimitiveArrayTransferExpressions(elementType, Expression.ArrayAccess(originalArray, indexes))
                                : GenerateFieldBasedComplexArrayTransferExpressions(elementType, nestedElementType, Expression.ArrayAccess(originalArray, indexes), nestedVariables, nestedExpressions);

                            nestedExpressions.Add(Expression.Assign(Expression.ArrayAccess(arrayClone, indexes), clonedElement));
                        }
                        else
                        {
                            nestedExpressions.Add(CloneExpressionHelper.CreateCopyComplexArrayTypeFieldExpression(Expression.ArrayAccess(originalArray, indexes), Expression.ArrayAccess(arrayClone, indexes), elementType, _objectDictionary));
                        }

                        // Whether array-in-array of reference-type-in-array, we need a null check before // doing anything to avoid NullReferenceExceptions for unset members
                        loopExpressions.Add(
                            Expression.IfThen(
                                Expression.NotEqual(Expression.ArrayAccess(originalArray, indexes),
                                                    Expression.Constant(null)),
                                Expression.Block(nestedVariables, nestedExpressions)));
                    }
                }
                else
                {
                    // Outer loops of any level just reset the inner loop's indexer and execute the inner loop
                    loopExpressions.Add(Expression.Assign(indexes[index + 1], Expression.Constant(0)));
                    loopExpressions.Add(innerLoop);
                }

                // Each time we executed the loop instructions, increment the indexer
                loopExpressions.Add(Expression.PreIncrementAssign(indexes[index]));

                // Build the loop using the expressions recorded above
                innerLoop = Expression.Loop(Expression.Block(loopVariables, loopExpressions), labels[index]);
            }

            // After the loop builder has finished, the innerLoop variable contains the entire hierarchy of nested loops, so add this to the clone expressions.
            arrayExpressions.Add(innerLoop);

            return(arrayClone);
        }