/// <summary> /// I decided to not use recursion to avoid stack overflow, what if object graph is too large? /// </summary> /// <param name="source"></param> /// <returns></returns> private object DeepClone(object source) { var state = CloneState.NextBreadcrumb; var value = source; var breadcrumbs = new Stack <Breadcrumb>(); Breadcrumb bcb = null; IChild child = null; Type type = null; // to avoid cyclic references, if objects already exists in target tree - use it var objectMap = new Hashtable(); switch (state) { case CloneState.NextChild: if (bcb.Childs.Length <= bcb.ChildIdx) { if (breadcrumbs.Count == 1) { return(bcb.TargetObject); } else { breadcrumbs.Pop(); var mappedValue = objectMap[bcb.SourceObject] = bcb.TargetObject; bcb = breadcrumbs.Peek(); child = bcb.Childs[bcb.ChildIdx]; child.SetValue(bcb.TargetObject, mappedValue); bcb.ChildIdx++; goto case CloneState.NextChild; } } child = bcb.Childs[bcb.ChildIdx]; value = child.GetValue(bcb.SourceObject); if (value == null || RequiresShallowClone(child, value)) { child.SetValue(bcb.TargetObject, value); bcb.ChildIdx++; goto case CloneState.NextChild; } else if (objectMap.ContainsKey(value)) { child.SetValue(bcb.TargetObject, objectMap[value]); bcb.ChildIdx++; goto case CloneState.NextChild; } else { goto case CloneState.NextBreadcrumb; } case CloneState.NextBreadcrumb: type = value.GetType(); if (type.IsArray) { var size = ((Array)value).Length; bcb = new Breadcrumb { Childs = GetArrayItems(size), TargetObject = Array.CreateInstance(type.GetElementType(), size), }; } else if (HasParameterlessConstructor(type)) { bcb = new Breadcrumb { TargetObject = Activator.CreateInstance(type), Childs = GetObjectChilds(type, value), }; } else { // ignore if (bcb != null) { child.SetValue(bcb.TargetObject, null); bcb.ChildIdx++; goto case CloneState.NextChild; } else { throw new NotSupportedException( "cloning type schould have parameterless constructor or be a one-dimensional array"); } } bcb.SourceObject = value; bcb.ChildIdx = 0; objectMap.Add(value, bcb.TargetObject); breadcrumbs.Push(bcb); goto case CloneState.NextChild; } return(source); }