static object Clone(object instance, VisitedGraph visited, object clone) { if (visited.ContainsKey(instance)) { return(visited[instance]); } visited.Add(instance, clone); Type type = instance.GetType(); while (type != null) { var ta = TypeAccessor.GetAccessor(type); foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { object value = ta.GetField(field.Name, instance); object cloneValue = visited.ContainsKey(value) ? visited[value] : Clone(value, visited); ta.SetField(field.Name, clone, cloneValue); } type = type.BaseType; } return(clone); }
/// <summary> /// Creates a deep copy of an object using the supplied dictionary of visited objects as /// a source of objects already encountered in the copy traversal. The dictionary of visited /// objects is used for holding objects that have already been copied, to avoid erroneous /// duplication of parts of the object graph. /// </summary> /// <param name="instance">The object to be copied.</param> /// <param name="visited">The graph of objects visited so far.</param> /// <param name="copy">The object to copy to.</param> /// <param name="expectedType">The type that should be found.</param> /// <param name="overrideSettings">Settings to override the copy logic.</param> /// <typeparam name="T">The type of the instance given.</typeparam> /// <returns>The copied object.</returns> private static T Clone <T>(this T instance, VisitedGraph visited, T copy, Type expectedType, IList <ICopyOverrideSettings> overrideSettings = null) { if (ShouldUseVisitedGraph(overrideSettings)) { if (visited.ContainsKey(instance)) { return((T)visited[instance]); } else { visited.Add(instance, copy); } } Type type = instance.GetType(); string instanceTypeNameString = type.FullName; bool shouldSkip = CheckIfShouldSkip(overrideSettings, type.FullName, false); if (shouldSkip) { return(default(T)); } BindingFlags flags = overrideSettings?.Any(s => s.IncludeNonPublic) ?? true ? BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance : BindingFlags.Public | BindingFlags.Instance; while (type != null) { List <ICopyOverrideSettings> currentSettings = overrideSettings?.Where(s => s.ContainingClassType.IsAssignableFrom(type)) .ToList() ?? new List <ICopyOverrideSettings>(); foreach (FieldInfo field in type.GetFields(flags)) { shouldSkip = false; shouldSkip = CheckIfShouldSkip(overrideSettings, field?.FieldType?.FullName, shouldSkip); if (shouldSkip || ShouldSkipDotNet(type, field.FieldType, field.Name)) { continue; } object value = field.GetValue(instance); ICopyOverrideSettings currentOverride = currentSettings.SingleOrDefault(s => s.FieldValueOverrideType.IsAssignableFrom(field.FieldType) && s.ContainingClassType.IsAssignableFrom(type) && s.AffectedFieldName == field.Name); try { if (currentOverride != null) { if (!currentOverride.ShouldSkipOverrideInsteadOfSet) { var settableValue = currentOverride.UseFieldValueOverrideFunction ? currentOverride.FieldValueOverrideFunction(instance) : currentOverride.FieldValueOverride; field.SetValue(copy, settableValue); if (currentOverride.OnlyOverrideFirst) { overrideSettings.Remove(overrideSettings.Single(s => s.AffectedFieldName == s.AffectedFieldName && s.ContainingClassType == s.ContainingClassType && s.FieldValueOverride == s.FieldValueOverride && s.FieldValueOverrideType == s.FieldValueOverrideType && s.OnlyOverrideFirst == s.OnlyOverrideFirst)); } } } else if (ShouldUseVisitedGraph(overrideSettings) && visited.ContainsKey(value)) { var settableValue = visited[value]; field.SetValue(copy, settableValue); } else { var settableValue = value.Clone(visited, field.FieldType, overrideSettings); field.SetValue(copy, settableValue); } } catch (Exception ex) { if (ShouldThrow(overrideSettings, field?.FieldType?.FullName, true, ex)) { throw; } } } foreach (PropertyInfo propertyInfo in type.GetProperties(flags)) { if (!propertyInfo.CanRead || !propertyInfo.CanWrite) { continue; } shouldSkip = false; shouldSkip = CheckIfShouldSkip(overrideSettings, propertyInfo?.PropertyType?.FullName, shouldSkip); ParameterInfo[] propParameters = propertyInfo.GetIndexParameters(); if (!shouldSkip && propParameters?.Where(pp => !pp.IsOptional).Count() > 0) { shouldSkip = true; } if (shouldSkip || ShouldSkipDotNet(type, propertyInfo.PropertyType, propertyInfo.Name)) { continue; } object value = propertyInfo.GetValue(instance); ICopyOverrideSettings currentOverride = currentSettings.SingleOrDefault(s => s.FieldValueOverrideType.IsAssignableFrom(propertyInfo.PropertyType) && s.ContainingClassType.IsAssignableFrom(type) && s.AffectedFieldName == propertyInfo.Name); try { if (currentOverride != null) { if (!currentOverride.ShouldSkipOverrideInsteadOfSet) { var settableValue = currentOverride.UseFieldValueOverrideFunction ? currentOverride.FieldValueOverrideFunction(instance) : currentOverride.FieldValueOverride; propertyInfo.SetValue(copy, settableValue); if (currentOverride.OnlyOverrideFirst) { overrideSettings.Remove(overrideSettings.Single(s => s.AffectedFieldName == s.AffectedFieldName && s.ContainingClassType == s.ContainingClassType && s.FieldValueOverride == s.FieldValueOverride && s.FieldValueOverrideType == s.FieldValueOverrideType && s.OnlyOverrideFirst == s.OnlyOverrideFirst)); } } } else if (ShouldUseVisitedGraph(overrideSettings) && visited.ContainsKey(value)) { var settableValue = visited[value]; propertyInfo.SetValue(copy, settableValue); } else { var settableValue = value.Clone(visited, propertyInfo.PropertyType, overrideSettings); propertyInfo.SetValue(copy, settableValue); } } catch (Exception ex) { if (ShouldThrow(overrideSettings, propertyInfo?.PropertyType?.FullName, true, ex)) { throw; } } } type = type.BaseType; } // call the post copy actions if (overrideSettings?.Any(s => s?.PostCopyActions != null) ?? false) { foreach (ICopyOverrideSettings setting in overrideSettings) { if (!(setting?.PostCopyActions?.Any() ?? false)) { continue; } // try the exact post copy action if (setting.PostCopyActions.ContainsKey(copy.GetType())) { setting.PostCopyActions[copy.GetType()](instance, copy, expectedType); } // try the default post copy action else if (setting.PostCopyActions.ContainsKey(setting.DefaultPostActionType)) { setting.PostCopyActions[setting.DefaultPostActionType](instance, copy, expectedType); } } } return(copy); }
private static object Clone(this object instance, VisitedGraph visited, object copy) { if (visited.ContainsKey(instance)) return visited[instance]; else visited.Add(instance, copy); Type type = instance.GetType(); while (type != null) { foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { object value = field.GetValue(instance); if (visited.ContainsKey(value)) field.SetValue(copy, visited[value]); else field.SetValue(copy, value.Clone(visited)); } type = type.BaseType; } return copy; }