Beispiel #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>
            private void GenerateFieldBasedComplexTypeTransferExpressions(Type complexType, Expression source, Expression target, ICollection <Expression> expression)
            {
                // Enumerate all of the type's fields and generate transfer expressions for each
                ISet <FieldInfo>        skipCloneFieldInfos;
                IEnumerable <FieldInfo> fieldInfos = GetFieldInfosIncludingBaseClasses(complexType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, out skipCloneFieldInfos);

                // For those field which skip deep copying, do shallow copying by assigning the field value from the source to the target
                foreach (FieldInfo fieldInfo in skipCloneFieldInfos)
                {
                    expression.Add(CloneExpressionHelper.CreateCopyFieldExpression(source, target, fieldInfo));
                }

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

                    if (IsTypePrimitiveOrString(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.
                        this.GenerateFieldBasedComplexTypeTransferExpressions(fieldType, Expression.Field(source, fieldInfo), Expression.Field(target, fieldInfo), expression);
                    }
                    else
                    {
                        this.GenerateFieldBasedReferenceTypeTransferExpressions(source, target, expression, fieldInfo);
                    }
                }
            }
Beispiel #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>
            private 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
                var fieldExpressions = new List <Expression>();
                var fieldVariables   = new List <ParameterExpression>();

                var 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)));
            }
Beispiel #3
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>
            private void GenerateFieldBasedComplexTypeTransferExpressions(Type complexType, Expression source,
                                                                          Expression target, ICollection <Expression> expression)
            {
                // Enumerate all of the type's fields and generate transfer expressions for each
                ISet <FieldInfo> skipCloneFieldInfos;
                var fieldInfos = GetFieldInfosIncludingBaseClasses(
                    complexType,
                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
                    out skipCloneFieldInfos);

                // For those field which skip deep copying, do shallow copying by assigning the field value from the source to the target
                foreach (var fieldInfo in skipCloneFieldInfos)
                {
                    expression.Add(CloneExpressionHelper.CreateCopyFieldExpression(source, target, fieldInfo));
                }

                foreach (var fieldInfo in fieldInfos)
                {
                    var fieldType = fieldInfo.FieldType;
                    if (fieldType.Equals(typeof(DataTable)))
                    {
                        expression.Add(CloneExpressionHelper.CreateCopyFieldExpression(source, target, fieldInfo));
                    }
                    else if (IsTypePrimitiveOrString(fieldType))
                    {
                        expression.Add(CloneExpressionHelper.CreateCopyFieldExpression(source, target, fieldInfo));
                    }
                    else if (fieldType.IsValueType)
                    {
                        GenerateFieldBasedComplexTypeTransferExpressions(fieldType, Expression.Field(source, fieldInfo),
                                                                         Expression.Field(target, fieldInfo), expression);
                    }
                    else
                    {
                        GenerateFieldBasedReferenceTypeTransferExpressions(source, target, expression, fieldInfo);
                    }
                }
            }
Beispiel #4
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>
            private 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();

                List <ParameterExpression> lengths = new List <ParameterExpression>();
                List <ParameterExpression> indexes = new List <ParameterExpression>();
                List <LabelTarget>         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)
                {
                    List <ParameterExpression> loopVariables   = new List <ParameterExpression>();
                    List <Expression>          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 (IsTypePrimitiveOrString(elementType))
                        {
                            loopExpressions.Add(Expression.Assign(Expression.ArrayAccess(arrayClone, indexes), Expression.ArrayAccess(originalArray, indexes)));
                        }
                        else if (elementType.IsValueType)
                        {
                            this.GenerateFieldBasedComplexTypeTransferExpressions(elementType, Expression.ArrayAccess(originalArray, indexes), Expression.ArrayAccess(arrayClone, indexes), loopExpressions);
                        }
                        else
                        {
                            List <ParameterExpression> nestedVariables   = new List <ParameterExpression>();
                            List <Expression>          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     = IsTypePrimitiveOrString(nestedElementType)
                                                               ? GenerateFieldBasedPrimitiveArrayTransferExpressions(elementType, Expression.ArrayAccess(originalArray, indexes))
                                                               : this.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, this._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);
            }