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 // We need a temporary variable in order to transfer the elements of the array
ParameterExpression clone = Expression.Variable(clonedType); ParameterExpression clone = Expression.Variable(clonedType);
variables.Add(clone); 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 dimensionCount = clonedType.GetArrayRank();
int baseVariableIndex = variables.Count; int baseVariableIndex = variables.Count;
@ -199,13 +193,13 @@ namespace Nuclex.Support.Cloning {
Expression.Assign( Expression.Assign(
length, length,
Expression.Call( 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) { switch(dimensionCount) {
case 1: { case 1: {
MethodInfo arrayCreateInstanceMethodInfo = typeof(Array).GetMethod( MethodInfo arrayCreateInstanceMethodInfo = typeof(Array).GetMethod(
@ -296,16 +290,7 @@ namespace Nuclex.Support.Cloning {
IList<ParameterExpression> variables, IList<ParameterExpression> variables,
ICollection<Expression> transferExpressions ICollection<Expression> transferExpressions
) { ) {
// To access the fields of the original type, we need it to be of the actual // Enumerate all of the type's fields and generate transfer expressions for each
// 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
FieldInfo[] fieldInfos = clonedType.GetFields( FieldInfo[] fieldInfos = clonedType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy BindingFlags.Instance | BindingFlags.FlattenHierarchy
@ -319,44 +304,58 @@ namespace Nuclex.Support.Cloning {
transferExpressions.Add( transferExpressions.Add(
Expression.Assign( Expression.Assign(
Expression.Field(clone, fieldInfo), Expression.Field(clone, fieldInfo),
Expression.Field(typedOriginal, fieldInfo) Expression.Field(original, fieldInfo)
) )
); );
} else if(fieldType.IsValueType) { } 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( generateComplexTypeTransferExpressions(
fieldType, fieldType,
Expression.Field(typedOriginal, fieldInfo), Expression.Field(original, fieldInfo),
Expression.Field(clone, fieldInfo), Expression.Field(clone, fieldInfo),
variables, variables,
transferExpressions transferExpressions
); );
} else { } 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 fieldTransferExpressions = new List<Expression>();
var fieldVariables = new List<ParameterExpression>(); var fieldVariables = new List<ParameterExpression>();
Expression fieldClone;
if(fieldType.IsArray) { if(fieldType.IsArray) {
// Arrays need to be cloned element-by-element
Type elementType = fieldType.GetElementType(); Type elementType = fieldType.GetElementType();
if(elementType.IsPrimitive || (elementType == typeof(string))) { if(elementType.IsPrimitive || (elementType == typeof(string))) {
// For primitive arrays, the Array.Clone() method is sufficient
fieldClone = generatePrimitiveArrayTransferExpressions( fieldClone = generatePrimitiveArrayTransferExpressions(
fieldType, fieldType,
Expression.Field(typedOriginal, fieldInfo), Expression.Field(original, fieldInfo),
fieldVariables, fieldVariables,
fieldTransferExpressions fieldTransferExpressions
); );
} else { } else {
// Arrays of complex types require manual cloning
fieldClone = generateComplexArrayTransferExpressions( fieldClone = generateComplexArrayTransferExpressions(
fieldType, fieldType,
Expression.Field(typedOriginal, fieldInfo), Expression.Field(original, fieldInfo),
fieldVariables, fieldVariables,
fieldTransferExpressions 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( fieldTransferExpressions.Add(
Expression.Assign(Expression.Field(clone, fieldInfo), fieldClone) Expression.Assign(Expression.Field(clone, fieldInfo), fieldClone)
); );
} else { } 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( MethodInfo getOrCreateClonerMethodInfo = typeof(ExpressionTreeCloner).GetMethod(
"getOrCreateDeepFieldBasedCloner", "getOrCreateDeepFieldBasedCloner",
BindingFlags.NonPublic | BindingFlags.Static BindingFlags.NonPublic | BindingFlags.Static
@ -364,6 +363,10 @@ namespace Nuclex.Support.Cloning {
MethodInfo getTypeMethodInfo = typeof(object).GetMethod("GetType"); MethodInfo getTypeMethodInfo = typeof(object).GetMethod("GetType");
MethodInfo invokeMethodInfo = typeof(Func<object, object>).GetMethod("Invoke"); MethodInfo invokeMethodInfo = typeof(Func<object, object>).GetMethod("Invoke");
// Generate expressions to do this:
// clone.SomeField = getOrCreateDeepFieldBasedCloner(
// original.SomeField.GetType()
// ).Invoke(original.SomeField);
fieldTransferExpressions.Add( fieldTransferExpressions.Add(
Expression.Assign( Expression.Assign(
Expression.Field(clone, fieldInfo), Expression.Field(clone, fieldInfo),
@ -372,11 +375,11 @@ namespace Nuclex.Support.Cloning {
Expression.Call( Expression.Call(
getOrCreateClonerMethodInfo, getOrCreateClonerMethodInfo,
Expression.Call( Expression.Call(
Expression.Field(typedOriginal, fieldInfo), getTypeMethodInfo Expression.Field(original, fieldInfo), getTypeMethodInfo
) )
), ),
invokeMethodInfo, invokeMethodInfo,
Expression.Field(typedOriginal, fieldInfo) Expression.Field(original, fieldInfo)
), ),
fieldType 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( transferExpressions.Add(
Expression.IfThen( Expression.IfThen(
Expression.NotEqual( Expression.NotEqual(
Expression.Field(typedOriginal, fieldInfo), Expression.Constant(null) Expression.Field(original, fieldInfo), Expression.Constant(null)
), ),
Expression.Block(fieldVariables, fieldTransferExpressions) Expression.Block(fieldVariables, fieldTransferExpressions)
) )
); );
} }
} }
} }
@ -421,10 +425,18 @@ namespace Nuclex.Support.Cloning {
) )
); );
} else { } 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 // Arrays of complex types require manual cloning
transferExpressions.Add( transferExpressions.Add(
generateComplexArrayTransferExpressions( 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 // Give it a new instance of the type being cloned
transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType))); 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 // Generate the expressions required to transfer the type field by field
generateComplexTypeTransferExpressions( 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 // Make sure the clone is the last thing in the block to set the return value