예제 #1
0
        /// <summary>
        /// Internal method to clone an object
        /// </summary>
        private static object CloneObjectInternal(object entity, object initiator, Dictionary <object, object> refValues)
        {
            //Null? No work
            if (entity == null)
            {
                return(null);
            }

            Type entityType = entity.GetType();

            //Special case value-types; they are passed by value so we have our
            //own copy right here.
            if (entityType.IsValueType)
            {
                return(entity);
            }

            //Clone strings (special case)
            if (entityType == typeof(string))
            {
                return(((string)entity).Clone());
            }

            //See if we've seen this object already.  If so, return the clone.
            if (refValues.ContainsKey(entity))
            {
                return(refValues[entity]);
            }

            //Clone weak references
            WeakReference weakReference = entity as WeakReference;

            if (weakReference != null)
            {
                if (weakReference.IsAlive)
                {
                    object clone = new WeakReference(weakReference.Target);
                    refValues[entity] = clone;
                    return(clone);
                }
                else
                {
                    object clone = new WeakReference(new object());
                    refValues[entity] = clone;
                    return(clone);
                }
            }

            //Use the ICloneable implemnetation of the object if possible
            if (entity != initiator)
            {
                ICloneable clonableInterface = entity as ICloneable;
                if (clonableInterface != null)
                {
                    object clone = clonableInterface.Clone();
                    refValues[entity] = clone;
                    return(clone);
                }
            }

            // If the type is [Serializable], then try that approach first; if serializable
            // fails (a child object is *not* serializable) then we will continue on.
            if (entityType.GetCustomAttributes(typeof(SerializableAttribute), true).Length > 0)
            {
                bool isSerializable = true;
                lock (s_notBinarySerializableLock)
                {
                    isSerializable = !s_notBinarySerializable.Contains(entityType);
                }

                if (isSerializable)
                {
                    ReusableMemoryStream memoryStream = ReusableMemoryStream.TakeMemoryStream();
                    try
                    {
                        BinaryFormatter binaryFormatter = new BinaryFormatter();
                        binaryFormatter.Serialize(memoryStream, entity);
                        memoryStream.Position = 0;
                        object clone = binaryFormatter.Deserialize(memoryStream);
                        refValues[entity] = clone;
                        return(clone);
                    }
                    catch (Exception)
                    {
                        lock (s_notBinarySerializableLock)
                        {
                            s_notBinarySerializable.Add(entityType);
                        }
                    }
                    finally
                    {
                        ReusableMemoryStream.ReregisterMemoryStream(memoryStream);
                    }
                }
            }

            //If the element is an array, then copy it.
            if (entityType.IsArray)
            {
                Array copy = (Array)((Array)entity).Clone();
                if (copy.Rank > 1)
                {
                    for (int rank = 0; rank < copy.Rank; rank++)
                    {
                        for (int i = copy.GetLowerBound(rank); i <= copy.GetUpperBound(rank); i++)
                        {
                            copy.SetValue(CloneObjectInternal(copy.GetValue(rank, i), initiator, refValues), rank, i);
                        }
                    }
                }
                else
                {
                    for (int i = copy.GetLowerBound(0); i <= copy.GetUpperBound(0); i++)
                    {
                        object value = copy.GetValue(i);
                        copy.SetValue(CloneObjectInternal(value, initiator, refValues), i);
                    }
                }
                refValues[entity] = copy;
                return(copy);
            }

            //Dictionary type
            if (entity is IDictionary)
            {
                IDictionary dictionary = (IDictionary)entity;
                IDictionary clone      = (IDictionary)Activator.CreateInstance(entityType);
                foreach (var key in dictionary.Keys)
                {
                    object keyCopy = CloneObjectInternal(key, initiator, refValues);
                    object valCopy = CloneObjectInternal(dictionary[key], initiator, refValues);
                    clone.Add(keyCopy, valCopy);
                }
                refValues[entity] = clone;
                return(clone);
            }

            //IList type
            if (entity is IList)
            {
                IList list  = (IList)entity;
                IList clone = (IList)Activator.CreateInstance(entityType);
                foreach (var value in list)
                {
                    object valCopy = CloneObjectInternal(value, initiator, refValues);
                    clone.Add(valCopy);
                }
                refValues[entity] = clone;
                return(clone);
            }

            //No obvious way to copy the object - do a field-by-field copy
            object result = Activator.CreateInstance(entityType);

            //Save off the reference
            refValues[entity] = result;

            //Walk through all the fields - this will capture auto-properties as well.
            Type actType = entityType;
            Dictionary <FieldInfo, object> alreadyScanned = new Dictionary <FieldInfo, object>();

            while (actType != null)
            {
                foreach (FieldInfo actField in actType.GetFields(BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public))
                {
                    //Check if this field is to be ignored during clone process
                    IgnoreGenericCloneAttribute ignoreCloneAttribute = Attribute.GetCustomAttribute(actField, typeof(IgnoreGenericCloneAttribute))
                                                                       as IgnoreGenericCloneAttribute;
                    if (ignoreCloneAttribute != null)
                    {
                        continue;
                    }

                    //Handling logic for fileds which should get a reference to the original object
                    AssignOriginalParentObjectAfterCloneAttribute assignOriginalAttribute = Attribute.GetCustomAttribute(actField, typeof(AssignOriginalParentObjectAfterCloneAttribute))
                                                                                            as AssignOriginalParentObjectAfterCloneAttribute;
                    if (assignOriginalAttribute != null)
                    {
                        try
                        {
                            actField.SetValue(result, entity);
                        }
                        catch (Exception ex)
                        {
                            throw new ApplicationException("Unable to set original object to field " + actField.Name + ": " + ex.Message, ex);
                        }
                        continue;
                    }

                    //Handle current field
                    if (!alreadyScanned.ContainsKey(actField))
                    {
                        actField.SetValue(result, CloneObjectInternal(actField.GetValue(entity), initiator, refValues));
                        alreadyScanned.Add(actField, null);
                    }
                }
                actType = actType.BaseType;
            }

            return(result);
        }