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> /// <returns></returns> private static object Clone(this object instance, VisitedGraph visited) { if (instance == null) return null; Type instanceType = instance.GetType(); if (instanceType.IsValueType || instanceType == typeof(string)) return instance; // Value types and strings are immutable else if (instanceType.IsArray) { int length = ((Array)instance).Length; Array copied = (Array)Activator.CreateInstance(instanceType, length); visited.Add(instance, copied); for (int i = 0; i < length; ++i) copied.SetValue(((Array)instance).GetValue(i).Clone(visited), i); return copied; } else return Clone(instance, visited, InstanceCreator.GetInstance(instanceType)); }
static object Clone(object instance, VisitedGraph visited) { if (instance == null) { return(null); } Type instanceType = instance.GetType(); if (instanceType.IsPointer || instanceType == typeof(Pointer) || instanceType.IsPrimitive || instanceType == typeof(string)) { return(instance); // Pointers, primitive types and strings are considered immutable } if (instanceType.GetInterface(CloneableInterface) != null) { return(((ICloneable)instance).Clone()); } if (typeof(Type).IsAssignableFrom(instanceType) || typeof(MemberInfo).IsAssignableFrom(instanceType) || typeof(ParameterInfo).IsAssignableFrom(instanceType)) { return(instance); } if (instanceType.IsArray) { var array = (Array)instance; int length = array.Length; Array copied = (Array)Activator.CreateInstance(instanceType, length); visited.Add(instance, copied); for (int i = 0; i < length; ++i) { var clone = Clone(array.GetValue(i), visited); copied.SetValue(clone, i); } return(copied); } return(Clone(instance, visited, CreateInstance(instanceType))); }
private static object Clone(this object instance, VisitedGraph visited, object copy) { visited.Add(instance, copy); var type = instance.GetType(); foreach (var field in GetTypeCopyableFieldList(type)) { object value = field.Getter(instance); object cloned; if (value == null) cloned = null; else if (!visited.TryGetValue(value, out cloned)) cloned = value.Clone(visited); field.Setter(copy, cloned); } return copy; }
/// <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); }
/// <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="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, Type expectedType, IList <ICopyOverrideSettings> overrideSettings = null) { if (instance == null) { return(default(T)); } Type instanceType = instance.GetType(); string instanceTypeNameString = instanceType.FullName; bool shouldSkip = false; shouldSkip = CheckIfShouldSkip(overrideSettings, instanceType.FullName, false); if (shouldSkip) { return(default(T)); } if (typeof(Type).IsAssignableFrom(instanceType)) { return(instance); } Tuple <bool, T> primitiveValue = instance.DeduceInstanceForTypeIfValueType(); if (primitiveValue.Item1) { return(primitiveValue.Item2); } if (instanceType.IsArray) { int length = ((Array)(object)instance).Length; Array copied = (Array)Activator.CreateInstance(instanceType, length); if (ShouldUseVisitedGraph(overrideSettings)) { visited.Add(instance, copied); } Type elementType = instance.GetType().GetElementType(); bool gotARealType = elementType != null; if (!gotARealType) { for (int i = 0; i < length || gotARealType; i++) { object current = ((Array)(object)instance).GetValue(i); if (current != null) { elementType = current.GetType(); gotARealType = true; } } } for (int i = 0; i < length; ++i) { object current = ((Array)(object)instance).GetValue(i); copied.SetValue(current.Clone(visited, gotARealType ? elementType : current?.GetType(), overrideSettings), i); } return((T)(object)copied); } else if (typeof(IList).IsAssignableFrom(instanceType)) { IList copied = (IList)Activator.CreateInstance(instanceType); Type elementType = null; bool gotAType = false; if (instanceType.IsGenericType) { elementType = instanceType.GetGenericArguments().Single(); gotAType = true; } if (!gotAType) { elementType = instance.GetType().GetElementType(); gotAType = true; } if (ShouldUseVisitedGraph(overrideSettings)) { visited.Add(instance, copied); } foreach (object item in (IList)instance) { object clonedItem = item.Clone(visited, elementType, overrideSettings); copied.Add(clonedItem); } return((T)(object)copied); } else if (typeof(IDictionary).IsAssignableFrom(instanceType)) { return(Clone(instance, visited, DeduceInstance(instance), expectedType, overrideSettings)); } else if (typeof(IEnumerable).IsAssignableFrom(instanceType)) { // This isn't a fully featured solution IEnumerable and ICollection can still be left out Type[] arguments = instanceType.GenericTypeArguments; IEnumerable copied = (IEnumerable)DeduceInstance(instance); if (ShouldUseVisitedGraph(overrideSettings)) { visited.Add(instance, copied); } List <object> test = new List <object>(); MethodInfo castMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public); MethodInfo castGenericMethod = castMethod.MakeGenericMethod(arguments); IEnumerable <dynamic> casted = (dynamic)castGenericMethod.Invoke(null, new object[] { ((IEnumerable)instance) }); copied = ((IEnumerable)instance).Cast <dynamic>().ToList().AsEnumerable(); return((T)(object)copied); } else if (typeof(DateTime).IsAssignableFrom(instanceType)) { if (((DateTime)(object)instance) == DateTime.MinValue) { // This is a min value allowed by SQL Server for some datetime fields return((T)(object)(new DateTime(1753, 1, 1))); } } return(Clone(instance, visited, DeduceInstance(instance), expectedType, overrideSettings)); }
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; }
/// <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> /// <returns></returns> private static object Clone(this object instance, VisitedGraph visited) { if (instance == null) return null; Type instanceType = instance.GetType(); if(typeof(Type).IsAssignableFrom(instanceType)) { return instance; } if (instanceType.IsPointer || instanceType == typeof(Pointer) || instanceType.IsPrimitive || instanceType == typeof(string)) return instance; // Pointers, primitive types and strings are considered immutable if (instanceType.IsArray) { int length = ((Array)instance).Length; Array copied = (Array)Activator.CreateInstance(instanceType, length); visited.Add(instance, copied); for (int i = 0; i < length; ++i) copied.SetValue(((Array)instance).GetValue(i).Clone(visited), i); return copied; } return Clone(instance, visited, DeduceInstance(instance)); }