diff --git a/Source/Cloning/ReflectionCloner.Test.cs b/Source/Cloning/ReflectionCloner.Test.cs
index 8ed684f..71c118d 100644
--- a/Source/Cloning/ReflectionCloner.Test.cs
+++ b/Source/Cloning/ReflectionCloner.Test.cs
@@ -76,6 +76,10 @@ namespace Nuclex.Support.Cloning {
public TestReferenceType ReferenceTypeField;
/// Reference type property for testing
public TestReferenceType ReferenceTypeProperty { get; set; }
+ /// An array field of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayField;
+ /// An array property of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
}
@@ -98,6 +102,10 @@ namespace Nuclex.Support.Cloning {
public TestReferenceType ReferenceTypeField;
/// Reference type property for testing
public TestReferenceType ReferenceTypeProperty { get; set; }
+ /// An array field of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayField;
+ /// An array property of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
}
@@ -399,6 +407,32 @@ namespace Nuclex.Support.Cloning {
return new HierarchicalValueType() {
TestField = 123,
TestProperty = 321,
+ ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 101, TestProperty = 202 },
+ null,
+ new TestReferenceType() { TestField = 909, TestProperty = 808 }
+ }
+ },
+ },
+ ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 303, TestProperty = 404 },
+ null,
+ new TestReferenceType() { TestField = 707, TestProperty = 606 }
+ }
+ },
+ },
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
@@ -424,6 +458,32 @@ namespace Nuclex.Support.Cloning {
return new HierarchicalReferenceType() {
TestField = 123,
TestProperty = 321,
+ ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 101, TestProperty = 202 },
+ null,
+ new TestReferenceType() { TestField = 909, TestProperty = 808 }
+ }
+ },
+ },
+ ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 303, TestProperty = 404 },
+ null,
+ new TestReferenceType() { TestField = 707, TestProperty = 606 }
+ }
+ },
+ },
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
diff --git a/Source/Cloning/ReflectionCloner.cs b/Source/Cloning/ReflectionCloner.cs
index b51d419..91e92e6 100644
--- a/Source/Cloning/ReflectionCloner.cs
+++ b/Source/Cloning/ReflectionCloner.cs
@@ -23,10 +23,8 @@ using System.Reflection;
namespace Nuclex.Support.Cloning {
- // TODO: Doesn't clone arrays yet
-
/// Clones objects using reflection
- public class ReflectionCloner : ICloneFactory, IStateCopier {
+ public class ReflectionCloner : ICloneFactory {
///
/// Creates a shallow clone of the specified object, reusing any referenced objects
@@ -38,22 +36,23 @@ namespace Nuclex.Support.Cloning {
///
/// A shallow clone of the provided object
public TCloned ShallowClone(TCloned objectToClone, bool usePropertyBasedClone) {
- if(typeof(TCloned).IsValueType) {
- TCloned clone = Activator.CreateInstance();
+ Type originalType = objectToClone.GetType();
+ if(originalType.IsPrimitive) {
+ return objectToClone; // Being value types, primitives are copied by default
+ } else if(originalType.IsArray) {
+ return (TCloned)shallowCloneArray(objectToClone);
+ } else if(originalType.IsValueType) {
if(usePropertyBasedClone) {
- shallowCopyValueTypePropertyBased(ref objectToClone, ref clone);
+ return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
} else {
- shallowCopyValueTypeFieldBased(ref objectToClone, ref clone);
+ return objectToClone; // Value types can be copied directly
}
- return clone;
} else {
- TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType());
if(usePropertyBasedClone) {
- shallowCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone);
+ return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
} else {
- shallowCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone);
+ return (TCloned)shallowCloneComplexFieldBased(objectToClone);
}
- return clone;
}
}
@@ -68,360 +67,296 @@ namespace Nuclex.Support.Cloning {
///
/// A deep clone of the provided object
public TCloned DeepClone(TCloned objectToClone, bool usePropertyBasedClone) {
- if(typeof(TCloned).IsValueType) {
- TCloned clone = Activator.CreateInstance();
- if(usePropertyBasedClone) {
- deepCopyValueTypePropertyBased(ref objectToClone, ref clone);
- } else {
- deepCopyValueTypeFieldBased(ref objectToClone, ref clone);
- }
- return clone;
+ if(usePropertyBasedClone) {
+ return (TCloned)deepCloneSinglePropertyBased(objectToClone);
} else {
- TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType());
- if(usePropertyBasedClone) {
- deepCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone);
- } else {
- deepCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone);
- }
- return clone;
+ return (TCloned)deepCloneSingleFieldBased(objectToClone);
}
}
- /// Transfers the state of one object into another
- /// Type of the object whose sate will be transferred
- /// Original instance the state will be taken from
- /// Target instance the state will be written to
- /// Whether to perform a property-based state copy
- public void ShallowCopyState(TState original, TState target, bool propertyBased)
- where TState : class {
- if(propertyBased) {
- shallowCopyReferenceTypePropertyBased(typeof(TState), original, target);
- } else {
- shallowCopyReferenceTypeFieldBased(typeof(TState), original, target);
- }
- }
+ /// Clones a complex type using field-based value transfer
+ /// Original instance that will be cloned
+ /// A clone of the original instance
+ private object shallowCloneComplexFieldBased(object original) {
+ Type originalType = original.GetType();
+ object clone = Activator.CreateInstance(originalType);
- /// Transfers the state of one object into another
- /// Type of the object whose sate will be transferred
- /// Original instance the state will be taken from
- /// Target instance the state will be written to
- /// Whether to perform a property-based state copy
- public void ShallowCopyState(
- ref TState original, ref TState target, bool propertyBased
- ) where TState : struct {
- if(propertyBased) {
- shallowCopyValueTypePropertyBased(ref original, ref target);
- } else {
- shallowCopyValueTypeFieldBased(ref original, ref target);
- }
- }
-
- ///
- /// Transfers the state of one object into another, creating clones of referenced objects
- ///
- /// Type of the object whose sate will be transferred
- /// Original instance the state will be taken from
- /// Target instance the state will be written to
- /// Whether to perform a property-based state copy
- public void DeepCopyState(TState original, TState target, bool propertyBased)
- where TState : class {
- if(propertyBased) {
- deepCopyReferenceTypePropertyBased(typeof(TState), original, target);
- } else {
- deepCopyReferenceTypeFieldBased(typeof(TState), original, target);
- }
- }
-
- ///
- /// Transfers the state of one object into another, creating clones of referenced objects
- ///
- /// Type of the object whose sate will be transferred
- /// Original instance the state will be taken from
- /// Target instance the state will be written to
- /// Whether to perform a property-based state copy
- public void DeepCopyState(ref TState original, ref TState target, bool propertyBased)
- where TState : struct {
- if(propertyBased) {
- deepCopyValueTypePropertyBased(ref original, ref target);
- } else {
- deepCopyValueTypeFieldBased(ref original, ref target);
- }
- }
-
- /// Creates a field-based shallow copy of a reference type
- /// Type the copy will be based upon
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void shallowCopyReferenceTypeFieldBased(
- Type copiedType, object original, object target
- ) {
- FieldInfo[] fieldInfos = copiedType.GetFields(
+ FieldInfo[] fieldInfos = originalType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
- fieldInfo.SetValue(target, fieldInfo.GetValue(original));
+ object originalValue = fieldInfo.GetValue(original);
+ if(originalValue != null) {
+ // Everything's just directly assigned in a shallow clone
+ fieldInfo.SetValue(clone, originalValue);
+ }
}
+
+ return clone;
}
- /// Creates a property-based shallow copy of a reference type
- /// Type the copy will be based upon
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void shallowCopyReferenceTypePropertyBased(
- Type copiedType, object original, object target
- ) {
- PropertyInfo[] propertyInfos = copiedType.GetProperties(
+ /// Clones a complex type using property-based value transfer
+ /// Original instance that will be cloned
+ /// A clone of the original instance
+ private object shallowCloneComplexPropertyBased(object original) {
+ Type originalType = original.GetType();
+ object clone = Activator.CreateInstance(originalType);
+
+ PropertyInfo[] propertyInfos = originalType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < propertyInfos.Length; ++index) {
PropertyInfo propertyInfo = propertyInfos[index];
- if(propertyInfo.CanRead && propertyInfo.CanWrite) {
- if(propertyInfo.PropertyType.IsPrimitive) {
- propertyInfo.SetValue(
- target,
- propertyInfo.GetValue(original, null),
- null
- );
- } else if(propertyInfo.PropertyType.IsValueType) {
- // Recurse into the value type - value types are seen as part of
- // the outer type and a shallow copy does follow their hierarchy.
- // This is equivalent to what would happen if you directly assign
- // one value type to another.
- object boxedOriginalValue = propertyInfo.GetValue(original, null);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- shallowCopyReferenceTypePropertyBased(
- originalType, boxedOriginalValue, boxedClonedValue
- );
- propertyInfo.SetValue(target, boxedClonedValue, null);
- } else {
- propertyInfo.SetValue(
- target,
- propertyInfo.GetValue(original, null),
- null
- );
- }
- }
- }
- }
-
- /// Creates a property-based shallow copy of a value type
- /// Value type that will be copied
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void shallowCopyValueTypePropertyBased(
- ref TState original, ref TState target
- ) {
- object boxedOriginal = original;
- object boxedTarget = target;
-
- PropertyInfo[] propertyInfos = typeof(TState).GetProperties(
- BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.FlattenHierarchy
- );
- for(int index = 0; index < propertyInfos.Length; ++index) {
- PropertyInfo propertyInfo = propertyInfos[index];
-
- if(propertyInfo.CanRead && propertyInfo.CanWrite) {
- if(propertyInfo.PropertyType.IsPrimitive) {
- propertyInfo.SetValue(
- boxedTarget,
- propertyInfo.GetValue(boxedOriginal, null),
- null
- );
- } else if(propertyInfo.PropertyType.IsValueType) {
- object boxedOriginalValue = propertyInfo.GetValue(original, null);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypePropertyBased(
- originalType, boxedOriginalValue, boxedClonedValue
- );
- propertyInfo.SetValue(boxedTarget, boxedClonedValue, null);
- } else {
- propertyInfo.SetValue(
- boxedTarget,
- propertyInfo.GetValue(boxedOriginal, null),
- null
- );
+ Type propertyType = propertyInfo.PropertyType;
+ object originalValue = propertyInfo.GetValue(original, null);
+ if(originalValue != null) {
+ if(propertyType.IsPrimitive) { // Primitive types can be assigned directly
+ propertyInfo.SetValue(clone, originalValue, null);
+ } else if(propertyType.IsValueType) {
+ // Value types are seen as part of the original type and are thus recursed into
+ propertyInfo.SetValue(clone, shallowCloneComplexPropertyBased(originalValue), null);
+ } else if(propertyType.IsArray) { // Arrays are assigned directly in a shallow clone
+ propertyInfo.SetValue(clone, originalValue, null);
+ } else { // Complex types are directly assigned without creating a copy
+ propertyInfo.SetValue(clone, originalValue, null);
}
}
}
- target = (TState)boxedTarget;
+ return clone;
}
- /// Creates a field-based shallow copy of a value type
- /// Value type that will be copied
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void shallowCopyValueTypeFieldBased(
- ref TState original, ref TState target
- ) {
- target = original; // hehe
+ /// Clones an array using field-based value transfer
+ /// Original array that will be cloned
+ /// A clone of the original array
+ private object shallowCloneArray(object original) {
+ return ((Array)original).Clone();
}
- /// Creates a field-based deep copy of a reference type
- /// Type the copy will be based upon
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void deepCopyReferenceTypeFieldBased(
- Type copiedType, object original, object target
- ) {
- FieldInfo[] fieldInfos = copiedType.GetFields(
+ /// Copies a single object using field-based value transfer
+ /// Original object that will be cloned
+ /// A clone of the original object
+ private static object deepCloneSingleFieldBased(object original) {
+ Type originalType = original.GetType();
+ if(originalType.IsPrimitive) {
+ return original; // Creates another box, does not reference boxed primitive
+ } else if(originalType.IsArray) {
+ return deepCloneArrayFieldBased((Array)original, originalType.GetElementType());
+ } else {
+ return deepCloneComplexFieldBased(original);
+ }
+ }
+
+ /// Clones a complex type using field-based value transfer
+ /// Original instance that will be cloned
+ /// A clone of the original instance
+ private static object deepCloneComplexFieldBased(object original) {
+ Type originalType = original.GetType();
+ object clone = Activator.CreateInstance(originalType);
+
+ FieldInfo[] fieldInfos = originalType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
- if(fieldInfo.FieldType.IsPrimitive) {
- fieldInfo.SetValue(target, fieldInfo.GetValue(original));
- } else if(fieldInfo.FieldType.IsValueType) {
- object boxedOriginalValue = fieldInfo.GetValue(original);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypeFieldBased(originalType, boxedOriginalValue, boxedClonedValue);
- fieldInfo.SetValue(target, boxedClonedValue);
- } else {
- object originalValue = fieldInfo.GetValue(original);
- if(originalValue != null) {
- Type originalType = originalValue.GetType();
- object clonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypeFieldBased(originalType, originalValue, clonedValue);
- fieldInfo.SetValue(target, clonedValue);
+ Type fieldType = fieldInfo.FieldType;
+ object originalValue = fieldInfo.GetValue(original);
+ if(originalValue != null) {
+ if(fieldType.IsPrimitive) { // Primitive types can be assigned directly
+ 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;
}
- /// Creates a property-based deep copy of a reference type
- /// Type the copy will be based upon
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void deepCopyReferenceTypePropertyBased(
- Type copiedType, object original, object target
- ) {
- PropertyInfo[] propertyInfos = copiedType.GetProperties(
+ /// Clones an array using field-based value transfer
+ /// Original array that will be cloned
+ /// Type of elements the original array contains
+ /// A clone of the original array
+ private static object deepCloneArrayFieldBased(Array original, Type elementType) {
+ if(elementType.IsPrimitive) {
+ return original.Clone();
+ }
+
+ int dimensionCount = original.Rank;
+
+ // Find out the length of each of the array's dimensions, also calculate how
+ // many elements there are in the array in total.
+ var lengths = new int[dimensionCount];
+ int totalElementCount = 0;
+ for(int index = 0; index < dimensionCount; ++index) {
+ lengths[index] = original.GetLength(index);
+ if(index == 0) {
+ totalElementCount = lengths[index];
+ } else {
+ totalElementCount *= lengths[index];
+ }
+ }
+
+ // Knowing the number of dimensions and the length of each dimension, we can
+ // create another array of the exact same sizes.
+ Array clone = Array.CreateInstance(elementType, lengths);
+
+ // If this is a one-dimensional array (most common type), do an optimized copy
+ // directly specifying the indices
+ if(dimensionCount == 1) {
+
+ // Clone each element of the array directly
+ for(int index = 0; index < totalElementCount; ++index) {
+ object originalElement = original.GetValue(index);
+ if(originalElement != null) {
+ clone.SetValue(deepCloneSingleFieldBased(originalElement), index);
+ }
+ }
+
+ } else { // Otherwise use the generic code for multi-dimensional arrays
+
+ var indices = new int[dimensionCount];
+ for(int index = 0; index < totalElementCount; ++index) {
+
+ // Determine the index for each of the array's dimensions
+ int elementIndex = index;
+ for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
+ indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
+ elementIndex /= lengths[dimensionIndex];
+ }
+
+ // Clone the current array element
+ object originalElement = original.GetValue(indices);
+ if(originalElement != null) {
+ clone.SetValue(deepCloneSingleFieldBased(originalElement), indices);
+ }
+
+ }
+
+ }
+
+ return clone;
+ }
+
+ /// Copies a single object using property-based value transfer
+ /// Original object that will be cloned
+ /// A clone of the original object
+ private static object deepCloneSinglePropertyBased(object original) {
+ Type originalType = original.GetType();
+ if(originalType.IsPrimitive) {
+ return original; // Creates another box, does not reference boxed primitive
+ } else if(originalType.IsArray) {
+ return deepCloneArrayPropertyBased((Array)original, originalType.GetElementType());
+ } else {
+ return deepCloneComplexPropertyBased(original);
+ }
+ }
+
+ /// Clones a complex type using property-based value transfer
+ /// Original instance that will be cloned
+ /// A clone of the original instance
+ private static object deepCloneComplexPropertyBased(object original) {
+ Type originalType = original.GetType();
+ object clone = Activator.CreateInstance(originalType);
+
+ PropertyInfo[] propertyInfos = originalType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < propertyInfos.Length; ++index) {
PropertyInfo propertyInfo = propertyInfos[index];
- if(propertyInfo.CanRead && propertyInfo.CanWrite) {
- if(propertyInfo.PropertyType.IsPrimitive) {
+ Type propertyType = propertyInfo.PropertyType;
+ object originalValue = propertyInfo.GetValue(original, null);
+ if(originalValue != null) {
+ if(propertyType.IsPrimitive) { // Primitive types can be assigned directly
+ propertyInfo.SetValue(clone, originalValue, null);
+ } else if(propertyType.IsArray) { // Arrays need to be cloned element-by-element
propertyInfo.SetValue(
- target,
- propertyInfo.GetValue(original, null),
+ clone,
+ deepCloneArrayPropertyBased((Array)originalValue, propertyType.GetElementType()),
null
);
- } else if(propertyInfo.PropertyType.IsValueType) {
- object boxedOriginalValue = propertyInfo.GetValue(original, null);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypePropertyBased(
- originalType, boxedOriginalValue, boxedClonedValue
- );
- propertyInfo.SetValue(target, boxedClonedValue, null);
- } else {
- object originalValue = propertyInfo.GetValue(original, null);
- if(originalValue != null) {
- Type originalType = originalValue.GetType();
- object clonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypePropertyBased(
- originalType, originalValue, clonedValue
- );
- propertyInfo.SetValue(target, clonedValue, null);
- }
+ } else { // Complex types need to be cloned member-by-member
+ propertyInfo.SetValue(clone, deepCloneSinglePropertyBased(originalValue), null);
}
}
}
+
+ return clone;
}
- /// Creates a field-based deep copy of a value type
- /// Value type that will be copied
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void deepCopyValueTypeFieldBased(
- ref TState original, ref TState target
- ) {
- object boxedOriginal = original;
- object boxedTarget = Activator.CreateInstance(original.GetType());
+ /// Clones an array using property-based value transfer
+ /// Original array that will be cloned
+ /// Type of elements the original array contains
+ /// A clone of the original array
+ private static object deepCloneArrayPropertyBased(Array original, Type elementType) {
+ if(elementType.IsPrimitive) {
+ return original.Clone();
+ }
- FieldInfo[] fieldInfos = typeof(TState).GetFields(
- BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.FlattenHierarchy
- );
- for(int index = 0; index < fieldInfos.Length; ++index) {
- FieldInfo fieldInfo = fieldInfos[index];
- if(fieldInfo.FieldType.IsPrimitive) {
- object boxedValue = fieldInfo.GetValue(boxedOriginal);
- fieldInfo.SetValue(boxedTarget, boxedValue);
- } else if(fieldInfo.FieldType.IsValueType) {
- object boxedOriginalValue = fieldInfo.GetValue(boxedOriginal);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypeFieldBased(originalType, boxedOriginalValue, boxedClonedValue);
- fieldInfo.SetValue(boxedTarget, boxedClonedValue);
+ int dimensionCount = original.Rank;
+
+ // Find out the length of each of the array's dimensions, also calculate how
+ // many elements there are in the array in total.
+ var lengths = new int[dimensionCount];
+ int totalElementCount = 0;
+ for(int index = 0; index < dimensionCount; ++index) {
+ lengths[index] = original.GetLength(index);
+ if(index == 0) {
+ totalElementCount = lengths[index];
} else {
- object originalValue = fieldInfo.GetValue(boxedOriginal);
- if(originalValue != null) {
- Type originalType = originalValue.GetType();
- object clonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypeFieldBased(originalType, originalValue, clonedValue);
- fieldInfo.SetValue(boxedTarget, clonedValue);
- }
+ totalElementCount *= lengths[index];
}
}
- target = (TState)boxedTarget;
- }
+ // Knowing the number of dimensions and the length of each dimension, we can
+ // create another array of the exact same sizes.
+ Array clone = Array.CreateInstance(elementType, lengths);
- /// Creates a property-based shallow copy of a value type
- /// Value type that will be copied
- /// Original object that will be copied
- /// Target object into which copied values will be written
- private static void deepCopyValueTypePropertyBased(
- ref TState original, ref TState target
- ) {
- object boxedOriginal = original;
- object boxedTarget = Activator.CreateInstance(original.GetType());
+ // If this is a one-dimensional array (most common type), do an optimized copy
+ // directly specifying the indices
+ if(dimensionCount == 1) {
- PropertyInfo[] propertyInfos = typeof(TState).GetProperties(
- BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.FlattenHierarchy
- );
- for(int index = 0; index < propertyInfos.Length; ++index) {
- PropertyInfo propertyInfo = propertyInfos[index];
- if(propertyInfo.CanRead && propertyInfo.CanWrite) {
- if(propertyInfo.PropertyType.IsPrimitive) {
- propertyInfo.SetValue(
- boxedTarget,
- propertyInfo.GetValue(boxedOriginal, null),
- null
- );
- } else if(propertyInfo.PropertyType.IsValueType) {
- object boxedOriginalValue = propertyInfo.GetValue(boxedOriginal, null);
- Type originalType = boxedOriginalValue.GetType();
- object boxedClonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypePropertyBased(
- originalType, boxedOriginalValue, boxedClonedValue
- );
- propertyInfo.SetValue(boxedTarget, boxedClonedValue, null);
- } else {
- object originalValue = propertyInfo.GetValue(boxedOriginal, null);
- if(originalValue != null) {
- Type originalType = originalValue.GetType();
- object clonedValue = Activator.CreateInstance(originalType);
- deepCopyReferenceTypePropertyBased(originalType, originalValue, clonedValue);
- propertyInfo.SetValue(boxedTarget, clonedValue, null);
- }
+ // Clone each element of the array directly
+ for(int index = 0; index < totalElementCount; ++index) {
+ object originalElement = original.GetValue(index);
+ if(originalElement != null) {
+ clone.SetValue(deepCloneSinglePropertyBased(originalElement), index);
}
}
+
+ } else { // Otherwise use the generic code for multi-dimensional arrays
+
+ var indices = new int[dimensionCount];
+ for(int index = 0; index < totalElementCount; ++index) {
+
+ // Determine the index for each of the array's dimensions
+ int elementIndex = index;
+ for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
+ indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
+ elementIndex /= lengths[dimensionIndex];
+ }
+
+ // Clone the current array element
+ object originalElement = original.GetValue(indices);
+ if(originalElement != null) {
+ clone.SetValue(deepCloneSinglePropertyBased(originalElement), indices);
+ }
+
+ }
+
}
- target = (TState)boxedTarget;
+ return clone;
}
}