Example #1
0
        /// <summary>
        /// Populate the fields of this object by copying them from equivalent fields on
        /// another object.  The fields to be copied must share names and types in order to
        /// be successfully transferred.
        /// The CopyAttribute will be used to determine the correct behaviour when copying fields
        /// accross - first on the field itself and then, if not set, on the type of the field.
        /// If neither of these is specified the default is to do a 'shallow' or reference-copy on all objects
        /// except for collection type, where the default is to not copy any fields *unless* they are
        /// specifically annotated.
        /// </summary>
        /// <param name="source">The object to copy fields from.</param>
        /// <param name="objectMap">A map of original objects to their copies.  Used when duplicating multiple
        /// objects at once to create links between them of the same relative relationships.</param>
        public static void CopyFieldsFrom(this object target, object source, ref Dictionary <object, object> objectMap)
        {
            Type                    targetType = target.GetType();
            Type                    sourceType = source.GetType();
            BindingFlags            flags      = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
            ICollection <FieldInfo> fields     = targetType.GetAllFields(flags);

            foreach (FieldInfo targetField in fields)
            {
                FieldInfo sourceField = sourceType.GetBaseField(targetField.Name, flags);
                if (sourceField != null && targetField.FieldType.IsAssignableFrom(sourceField.FieldType))
                {
                    // Have found a matching property - check for copy behaviour attributes:
                    // Currently this is done on the source field.  Might it also be safer to check the target
                    // field as well, for at least certain values?
                    CopyAttribute copyAtt = sourceField.GetAttribute <CopyAttribute>();
                    // If copy attribute is not set on the field, we will try it on the type:
                    if (copyAtt == null)
                    {
                        copyAtt = sourceField.FieldType.GetCustomAttribute <CopyAttribute>();
                    }

                    CopyBehaviour behaviour      = CopyBehaviour.COPY;
                    CopyBehaviour itemsBehaviour = CopyBehaviour.COPY;
                    if (sourceType.IsCollection())
                    {
                        behaviour = CopyBehaviour.DO_NOT_COPY; //By default, do not copy fields from collection types
                    }
                    if (copyAtt != null)
                    {
                        behaviour = copyAtt.Behaviour;
                        if (copyAtt is CollectionCopyAttribute)
                        {
                            itemsBehaviour = ((CollectionCopyAttribute)copyAtt).ItemsBehaviour;
                        }
                    }

                    if (behaviour != CopyBehaviour.DO_NOT_COPY)
                    {
                        object value = sourceField.GetValue(source);
                        value = ValueToAssign(value, ref behaviour, itemsBehaviour, ref objectMap);
                        if (behaviour != CopyBehaviour.DO_NOT_COPY)
                        {
                            targetField.SetValue(target, value);
                        }
                    }
                }
            }
        }
Example #2
0
        /// <summary>
        /// Produce a duplicated copy of this object.
        /// Family references will be copied, save for those which
        /// are intended to be unique to this object, which will themselves
        /// be duplicated.
        /// </summary>
        /// <param name="objectMap">The map of original objects to duplicated objects.</param>
        /// <returns>A duplicated copy of this object</returns>
        public static T Duplicate <T>(this T obj, ref Dictionary <object, object> objectMap, CopyBehaviour itemsBehaviour = CopyBehaviour.COPY)
            where T : IDuplicatable
        {
            T clone;

            if (obj.GetType().HasParameterlessConstructor())
            {
                clone = (T)Activator.CreateInstance(obj.GetType(), true); //Create a blank instance of the relevant type
            }
            else
            {
#if !JS
                // As a (potentially dangerous) fallback:
                clone = (T)FormatterServices.GetUninitializedObject(obj.GetType());
                //Special cludge for Uniques to avoid having all-0 GUIDs:
                if (clone is IUniqueWithModifiableGUID)
                {
                    ((IUniqueWithModifiableGUID)clone).SetGUID(Guid.NewGuid());
                }
#else
                throw new NotSupportedException("Class to be duplicated does not provide a parameterless constructor!");
#endif
            }

            if (objectMap == null)
            {
                objectMap = new Dictionary <object, object>();
            }
            objectMap[obj] = clone; //Store the original-clone relationship in the map
            clone.CopyFieldsFrom(obj, ref objectMap);

            if (obj.GetType().IsCollection() && itemsBehaviour != CopyBehaviour.DO_NOT_COPY)
            {
                ICollection source = (ICollection)obj;
                ICollection target = (ICollection)clone;
                foreach (object item in source)
                {
                    CopyBehaviour behaviour         = itemsBehaviour;
                    CopyBehaviour subItemsBehaviour = itemsBehaviour;
                    if (item != null && item is IDuplicatable)
                    {
                        CopyAttribute cAtt = item.GetType().GetCustomAttribute <CopyAttribute>();
                        if (cAtt != null)
                        {
                            behaviour = cAtt.Behaviour;
                            if (cAtt is CollectionCopyAttribute)
                            {
                                subItemsBehaviour = ((CollectionCopyAttribute)cAtt).ItemsBehaviour;
                            }
                        }
                    }
                    object value = ValueToAssign(item, ref behaviour, subItemsBehaviour, ref objectMap);
                    if (target is IList)
                    {
                        ((IList)target).Add(value);
                    }
                    // TODO: Other types of collections?
                }
            }

            return(clone);
        }