#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Reflection; namespace Nuclex.Support.Cloning { // TODO: Doesn't clone arrays yet /// Clones objects using reflection public class ReflectionCloner : ICloneFactory, IStateCopier { /// /// Creates a shallow clone of the specified object, reusing any referenced objects /// /// Type of the object that will be cloned /// Object that will be cloned /// /// Whether to clone the object based on its properties only /// /// A shallow clone of the provided object public TCloned ShallowClone(TCloned objectToClone, bool usePropertyBasedClone) { if(typeof(TCloned).IsValueType) { TCloned clone = Activator.CreateInstance(); if(usePropertyBasedClone) { shallowCopyValueTypePropertyBased(ref objectToClone, ref clone); } else { shallowCopyValueTypeFieldBased(ref objectToClone, ref clone); } return clone; } else { TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType()); if(usePropertyBasedClone) { shallowCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone); } else { shallowCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone); } return clone; } } /// /// Creates a deep clone of the specified object, also creating clones of all /// child objects being referenced /// /// Type of the object that will be cloned /// Object that will be cloned /// /// Whether to clone the object based on its properties only /// /// 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; } else { TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType()); if(usePropertyBasedClone) { deepCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone); } else { deepCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone); } return clone; } } /// 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); } } /// 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( 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)); } } /// 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( 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 ); } } } target = (TState)boxedTarget; } /// 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 } /// 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( 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); } } } } /// 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( 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) { 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); } } } } } /// 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()); 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); } 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); } } } target = (TState)boxedTarget; } /// 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()); 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); } } } } target = (TState)boxedTarget; } } } // namespace Nuclex.Support.Cloning