Got rid of some unnecessary downcasts for the expression tree cloner

git-svn-id: file:///srv/devel/repo-conversion/nusu@235 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-02-06 09:04:10 +00:00
parent 62d0c6611c
commit f5c282e840

View File

@ -178,12 +178,6 @@ namespace Nuclex.Support.Cloning {
// We need a temporary variable in order to transfer the elements of the array
ParameterExpression clone = Expression.Variable(clonedType);
variables.Add(clone);
ParameterExpression typedOriginal = Expression.Variable(clonedType);
variables.Add(typedOriginal);
transferExpressions.Add(
Expression.Assign(typedOriginal, Expression.Convert(original, clonedType))
);
int dimensionCount = clonedType.GetArrayRank();
int baseVariableIndex = variables.Count;
@ -199,13 +193,13 @@ namespace Nuclex.Support.Cloning {
Expression.Assign(
length,
Expression.Call(
typedOriginal, arrayGetLengthMethodInfo, Expression.Constant(index)
original, arrayGetLengthMethodInfo, Expression.Constant(index)
)
)
);
}
// Create a new array of identical size
// Create a new array of identical size and dimensions
switch(dimensionCount) {
case 1: {
MethodInfo arrayCreateInstanceMethodInfo = typeof(Array).GetMethod(
@ -296,16 +290,7 @@ namespace Nuclex.Support.Cloning {
IList<ParameterExpression> variables,
ICollection<Expression> transferExpressions
) {
// 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))
);
// Now enumerate all of the type's fields and generate transfer expressions for
// each of them
// Enumerate all of the type's fields and generate transfer expressions for each
FieldInfo[] fieldInfos = clonedType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
@ -319,44 +304,58 @@ namespace Nuclex.Support.Cloning {
transferExpressions.Add(
Expression.Assign(
Expression.Field(clone, fieldInfo),
Expression.Field(typedOriginal, 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.
generateComplexTypeTransferExpressions(
fieldType,
Expression.Field(typedOriginal, fieldInfo),
Expression.Field(original, fieldInfo),
Expression.Field(clone, fieldInfo),
variables,
transferExpressions
);
} else {
// 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
Expression fieldClone;
var fieldTransferExpressions = new List<Expression>();
var fieldVariables = new List<ParameterExpression>();
Expression fieldClone;
if(fieldType.IsArray) {
// Arrays need to be cloned element-by-element
Type elementType = fieldType.GetElementType();
if(elementType.IsPrimitive || (elementType == typeof(string))) {
// For primitive arrays, the Array.Clone() method is sufficient
fieldClone = generatePrimitiveArrayTransferExpressions(
fieldType,
Expression.Field(typedOriginal, fieldInfo),
Expression.Field(original, fieldInfo),
fieldVariables,
fieldTransferExpressions
);
} else {
// Arrays of complex types require manual cloning
fieldClone = generateComplexArrayTransferExpressions(
fieldType,
Expression.Field(typedOriginal, fieldInfo),
Expression.Field(original, fieldInfo),
fieldVariables,
fieldTransferExpressions
);
}
// Add the assignment to the transfer expressions. The array transfer expression
// generator will either have set up a temporary variable to hold the array or
// returned the conversion expression straight away
fieldTransferExpressions.Add(
Expression.Assign(Expression.Field(clone, fieldInfo), fieldClone)
);
} else {
// Complex types are cloned by checking their actual, concrete type (fields
// may be typed to an interface or base class) and requesting a cloner for that
// type during runtime
MethodInfo getOrCreateClonerMethodInfo = typeof(ExpressionTreeCloner).GetMethod(
"getOrCreateDeepFieldBasedCloner",
BindingFlags.NonPublic | BindingFlags.Static
@ -364,6 +363,10 @@ namespace Nuclex.Support.Cloning {
MethodInfo getTypeMethodInfo = typeof(object).GetMethod("GetType");
MethodInfo invokeMethodInfo = typeof(Func<object, object>).GetMethod("Invoke");
// Generate expressions to do this:
// clone.SomeField = getOrCreateDeepFieldBasedCloner(
// original.SomeField.GetType()
// ).Invoke(original.SomeField);
fieldTransferExpressions.Add(
Expression.Assign(
Expression.Field(clone, fieldInfo),
@ -372,11 +375,11 @@ namespace Nuclex.Support.Cloning {
Expression.Call(
getOrCreateClonerMethodInfo,
Expression.Call(
Expression.Field(typedOriginal, fieldInfo), getTypeMethodInfo
Expression.Field(original, fieldInfo), getTypeMethodInfo
)
),
invokeMethodInfo,
Expression.Field(typedOriginal, fieldInfo)
Expression.Field(original, fieldInfo)
),
fieldType
)
@ -384,15 +387,16 @@ namespace Nuclex.Support.Cloning {
);
}
// Wrap up the generated array or complex reference type transfer expressions
// in a null check so the field is skipped if it is not holding an instance.
transferExpressions.Add(
Expression.IfThen(
Expression.NotEqual(
Expression.Field(typedOriginal, fieldInfo), Expression.Constant(null)
Expression.Field(original, fieldInfo), Expression.Constant(null)
),
Expression.Block(fieldVariables, fieldTransferExpressions)
)
);
}
}
}
@ -421,10 +425,18 @@ namespace Nuclex.Support.Cloning {
)
);
} else {
// 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))
);
// Arrays of complex types require manual cloning
transferExpressions.Add(
generateComplexArrayTransferExpressions(
clonedType, original, variables, transferExpressions
clonedType, typedOriginal, variables, transferExpressions
)
);
}
@ -437,9 +449,17 @@ namespace Nuclex.Support.Cloning {
// Give it a new instance of the type being cloned
transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType)));
// 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))
);
// Generate the expressions required to transfer the type field by field
generateComplexTypeTransferExpressions(
clonedType, original, clone, variables, transferExpressions
clonedType, typedOriginal, clone, variables, transferExpressions
);
// Make sure the clone is the last thing in the block to set the return value