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:
parent
62d0c6611c
commit
f5c282e840
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user