private static void SearchAndReplaceAggregates(object root)
        {
            var visitedItems = new HashSet <object>();
            var queue        = new Queue <object>();

            queue.Enqueue(root);

            while (queue.Count > 0)
            {
                var inst = queue.Dequeue();

                if (IsInstanceOfSimpleType(inst))
                {
                    continue;
                }

                // geometries
                var baseType = inst.GetType().BaseType;
                if ((baseType != null && baseType.Name == "Geometry") && inst is ICloneable)
                {
                    continue;
                }

                // gdi stuff
                if (inst.GetType().FullName.StartsWith("System.Drawing.") && inst is ICloneable)
                {
                    continue;
                }

                if (visitedItems.Contains(inst))
                {
                    continue;
                }
                visitedItems.Add(inst);

                foreach (var pi in GetAllAccessiblePropertiesForType(inst.GetType()))
                {
                    object value;
                    if (!GetValue(inst, pi, out value))
                    {
                        continue;
                    }

                    if (AttributeInfoCache.IsAggregation(pi))
                    {
                        if (value is IList)           //lists are special case of aggregation
                        {
                            var clone = (IList)value; //already cloned..just not the values

                            for (var i = 0; i < clone.Count; i++)
                            {
                                var item       = clone[i];
                                var clonedItem = cloneStore.GetExistingCloneFor(item);
                                if (clonedItem != null)
                                {
                                    ReplaceListItem(clone, i, clonedItem);
                                }
                            }
                        }
                        else
                        {
                            var clone = cloneStore.GetExistingCloneFor(value);
                            if (clone != null)
                            {
                                SetValue(pi, inst, clone);
                            }
                        }
                    }
                    else
                    {
                        queue.Enqueue(value);
                    }
                }
                if (inst is IList)
                {
                    var list = inst as IList;
                    foreach (var item in list)
                    {
                        queue.Enqueue(item);
                    }
                }
            }
        }
        private static void DeepClonePropertiesAndFields(object inst, object clone)
        {
            var aggregationProperties = new List <string>();

            // set in all properties (to trigger subscription etc)
            foreach (var pi in OrderByComplexity(GetAllAccessiblePropertiesForType(inst.GetType())))
            {
                object value;
                if (!GetValue(inst, pi, out value))
                {
                    continue;
                }

                object clonedValue;

                if (AttributeInfoCache.IsAggregation(pi)) //if aggregate: only set existing clones
                {
                    aggregationProperties.Add(pi.Name);

                    if (ProcessAggregateMember(value, clone, pi, out clonedValue))
                    {
                        continue;
                    }
                }
                else
                {
                    clonedValue = DeepCloneCore(value);
                }

                SetValue(pi, clone, clonedValue);
            }

            // set in all fields that we missed so far
            foreach (var fieldAndValue in GetNonInfrastructureFields(inst))
            {
                var fi    = fieldAndValue.Key;
                var value = fieldAndValue.Value;

                if (!ShouldReplaceValue(value))
                {
                    continue;
                }

                if (!IsInstanceOfSimpleType(value))
                {
                    if (cloneStore.IsAlreadyClonedInstance(value))
                    {
                        continue;
                    }
                }

                object clonedValue;
                if (aggregationProperties.Any(p => IsFieldForProperty(fi, p)))
                {
                    if (ProcessAggregateMember(value, clone, fi, out clonedValue))
                    {
                        continue;
                    }
                }
                else
                {
                    clonedValue = DeepCloneCore(value);
                }

                SetValue(fi, clone, clonedValue);
            }
        }