Monday, February 17, 2014

C#: Deep Clone an Object onto another Object of the same type


        /// <summary>
        ///     Copy each field in the <paramref name="source" /> to the matching field in the <paramref name="destination" />.
        ///     then
        ///     Copy each property in the <paramref name="source" /> to the matching property in the
        ///     <paramref name="destination" />.
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        public static Boolean DeepClone< TSource >( this TSource source, TSource destination ) {
            if ( ReferenceEquals( source, destination ) ) {
                return false;
            }
            if ( Equals( source, default( TSource ) ) ) {
                return false;
            }
            if ( Equals( destination, default( TSource ) ) ) {
                return false;
            }
            //copy all settable fields
            // then
            //copy all settable properties (going on the assumption that properties are the ones modifiying their private fields).
            return CopyFields( source: source, destination: destination ) && CopyProperties( source: source, destination: destination );
        }
 
        /// <summary>
        ///     Copy the value of each field of the <paramref name="source" /> to the matching field in the
        ///     <paramref
        ///         name="destination" />
        ///     .
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        public static Boolean CopyFields< TSource >( this TSource source, TSource destination ) {
            try {
                var sourceFields = source.GetType().GetAllFields();
                var destFields = destination.GetType().GetAllFields();
 
                foreach ( var field in sourceFields.Where( destFields.Contains ) ) {
                    CopyField( source: source, destination: destination, field: field );
                }
                return true;
            }
            catch ( Exception ) {
                return false;
            }
        }
 
        /// <summary>
        ///     Enumerate all fields of the <paramref name="type" />
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable< FieldInfo > GetAllFields( this Type type ) {
            if ( null == type ) {
                return Enumerable.Empty< FieldInfo >();
            }
 
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            return type.GetFields( flags ).Union( GetAllFields( type.BaseType ) );
        }
 
        public static void CopyField< TSource >( this TSource source, TSource destination, [NotNull] FieldInfo field, Boolean mergeDictionaries = true ) {
            if ( field == null ) {
                throw new ArgumentNullException( "field" );
            }
            try {
                var sourceValue = field.GetValue( source );
                if ( mergeDictionaries ) {
                    var sourceAsDictionary = sourceValue as IDictionary;
                    if ( null == sourceAsDictionary ) {
                        return;
                    }
                    var destAsDictionary = field.GetValue( destination ) as IDictionary;
                    if ( null == destAsDictionary ) {
                        return;
                    }
                    foreach ( var key in sourceAsDictionary.Keys ) {
                        try {
                            destAsDictionary[ key ] = sourceAsDictionary[ key ];
                        }
                        catch ( Exception exception ) {
                            exception.Log();
                        }
                    }
                    return;
                }
 
                field.SetValue( destination, sourceValue );
            }
            catch ( TargetException exception ) {
                exception.Log();
            }
            catch ( NotSupportedException exception ) {
                exception.Log();
            }
            catch ( FieldAccessException exception ) {
                exception.Log();
            }
            catch ( ArgumentException exception ) {
                exception.Log();
            }
        }
 
        /// <summary>
        ///     Copy the value of each get property of the <paramref name="source" /> to each set property of the
        ///     <paramref
        ///         name="destination" />
        ///     .
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        public static Boolean CopyProperties< TSource >( this TSource source, TSource destination ) {
            try {
                var sourceProps = source.GetType().GetAllProperties().Where( prop => prop.CanRead );
                var destProps = destination.GetType().GetAllProperties().Where( prop => prop.CanWrite );
                foreach ( var prop in sourceProps.Where( destProps.Contains ) ) {
                    CopyProperty( source: source, destination: destination, prop: prop );
                }
                return true;
            }
            catch ( Exception ) {
                return false;
            }
        }
 
        /// <summary>
        ///     Enumerate all properties of the <paramref name="type" />
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable< PropertyInfo > GetAllProperties( this Type type ) {
            if ( null == type ) {
                return Enumerable.Empty< PropertyInfo >();
            }
 
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            return type.GetProperties( flags ).Union( GetAllProperties( type.BaseType ) );
        }
 
        public static void CopyProperty< TSource >( this TSource source, TSource destination, PropertyInfo prop ) {
            try {
                var sourceValue = prop.GetValue( source, null );
                prop.SetValue( destination, sourceValue, null );
            }
            catch ( TargetException exception ) {
                exception.Log();
            }
            catch ( NotSupportedException exception ) {
                exception.Log();
            }
            catch ( FieldAccessException exception ) {
                exception.Log();
            }
            catch ( ArgumentException exception ) {
                exception.Log();
            }
        }

No comments: