public CopyableNumbersWithSettings(int a, float b, ICopyOverrideSettings settings)
     : base(a, b, settings)
 {
     _a        = a;
     _b        = b;
     _settings = settings;
 }
 /// <summary>
 /// Creates a copy of the object.
 /// </summary>
 /// <param name="instance">The object to be copied.</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>A deep copy of the object.</returns>
 public static T Copy <T>(this T instance, ICopyOverrideSettings overrideSettings)
 {
     return(instance.Copy(typeof(T), overrideSettings));
 }
 /// <summary>
 /// Creates a deep copy of the object using the supplied object as a target for the copy operation.
 /// </summary>
 /// <param name="instance">The object to be copied.</param>
 /// <param name="copy">The object to copy values to. All fields of this object will be overwritten.</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>A deep copy of the object.</returns>
 public static T Copy <T>(this T instance, T copy, Type expectedType, ICopyOverrideSettings overrideSettings)
 {
     return(instance.Copy(copy, expectedType, new[] { overrideSettings }));
 }
        /// <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);
        }