/// <summary>Clones a complex type using field-based value transfer</summary> /// <param name="original">Original instance that will be cloned</param> /// <returns>A clone of the original instance</returns> private static object shallowCloneComplexFieldBased(object original) { Type originalType = original.GetType(); #if (XBOX360 || WINDOWS_PHONE) object clone = Activator.CreateInstance(originalType); #else object clone = FormatterServices.GetUninitializedObject(originalType); #endif FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ); for (int index = 0; index < fieldInfos.Length; ++index) { FieldInfo fieldInfo = fieldInfos[index]; object originalValue = fieldInfo.GetValue(original); if (originalValue != null) { // Everything's just directly assigned in a shallow clone fieldInfo.SetValue(clone, originalValue); } } return(clone); }
/// <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 FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ); for (int index = 0; index < fieldInfos.Length; ++index) { FieldInfo fieldInfo = fieldInfos[index]; Type 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 ); } } }
/// <summary>Clones a complex type using field-based value transfer</summary> /// <param name="original">Original instance that will be cloned</param> /// <returns>A clone of the original instance</returns> private static object deepCloneComplexFieldBased(object original) { Type originalType = original.GetType(); #if (XBOX360 || WINDOWS_PHONE) object clone = Activator.CreateInstance(originalType); #else object clone = FormatterServices.GetUninitializedObject(originalType); #endif FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ); for (int index = 0; index < fieldInfos.Length; ++index) { FieldInfo fieldInfo = fieldInfos[index]; Type fieldType = fieldInfo.FieldType; object originalValue = fieldInfo.GetValue(original); if (originalValue != null) { // Primitive types can be assigned directly if (fieldType.IsPrimitive || (fieldType == typeof(string))) { fieldInfo.SetValue(clone, originalValue); } else if (fieldType.IsArray) // Arrays need to be cloned element-by-element { fieldInfo.SetValue( clone, deepCloneArrayFieldBased((Array)originalValue, fieldType.GetElementType()) ); } else // Complex types need to be cloned member-by-member { fieldInfo.SetValue(clone, deepCloneSingleFieldBased(originalValue)); } } } return(clone); }
/// <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) { ParameterExpression 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 ParameterExpression 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 ParameterExpression 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 MethodInfo 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 FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ); for (int index = 0; index < fieldInfos.Length; ++index) { FieldInfo fieldInfo = fieldInfos[index]; transferExpressions.Add( 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()); }