From 7f7b9eff19268de7d90a6359b90b75f365a15a99 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Thu, 2 Feb 2012 17:59:34 +0000 Subject: [PATCH] Reflection-based cloner now also clones arrays (including deep clones of array values if desired) git-svn-id: file:///srv/devel/repo-conversion/nusu@225 d2e56fa2-650e-0410-a79f-9358c0239efd --- Source/Cloning/ReflectionCloner.Test.cs | 60 +++ Source/Cloning/ReflectionCloner.cs | 547 +++++++++++------------- 2 files changed, 301 insertions(+), 306 deletions(-) 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; } }