public static object MakeDeepCopy(object source, Type resultType, Func <string, Traverse, Traverse, object> processor = null, string pathRoot = "") { if (source == null) { return(null); } var type = source.GetType(); if (type.IsPrimitive) { return(source); } if (type.IsEnum) { return(Enum.ToObject(resultType, (int)source)); } if (type.IsGenericType && resultType.IsGenericType) { var addOperation = FirstMethod(resultType, m => m.Name == "Add" && m.GetParameters().Count() == 1); if (addOperation != null) { var addableResult = Activator.CreateInstance(resultType); var addInvoker = MethodInvoker.GetHandler(addOperation); var newElementType = resultType.GetGenericArguments()[0]; var i = 0; foreach (var element in source as IEnumerable) { var iStr = (i++).ToString(); var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; var newElement = MakeDeepCopy(element, newElementType, processor, path); addInvoker(addableResult, new object[] { newElement }); } return(addableResult); } // TODO: add dictionaries support // maybe use methods in Dictionary<KeyValuePair<TKey,TVal>> } if (type.IsArray && resultType.IsArray) { var elementType = resultType.GetElementType(); var length = ((Array)source).Length; var arrayResult = Activator.CreateInstance(resultType, new object[] { length }) as object[]; var originalArray = source as object[]; for (var i = 0; i < length; i++) { var iStr = i.ToString(); var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; arrayResult[i] = MakeDeepCopy(originalArray[i], elementType, processor, path); } return(arrayResult); } var ns = type.Namespace; if (ns == "System" || (ns?.StartsWith("System.") ?? false)) { return(source); } var result = CreateInstance(resultType); Traverse.IterateFields(source, result, (name, src, dst) => { var path = pathRoot.Length > 0 ? pathRoot + "." + name : name; var value = processor != null ? processor(path, src, dst) : src.GetValue(); dst.SetValue(MakeDeepCopy(value, dst.GetValueType(), processor, path)); }); return(result); }
public Traverse(Traverse traverse) { this.traverse = traverse; }