public AttributeCloner(
            TAttribute source,
            BindingDataContract bindingDataContract,
            INameResolver nameResolver = null,
            Func <TAttribute, Task <TAttribute> > hook = null)
        {
            _hook        = hook;
            nameResolver = nameResolver ?? new EmptyNameResolver();
            _source      = source;

            Type attributeType = typeof(TAttribute);

            PropertyInfo[] allProperties = attributeType.GetProperties(Flags);

            // Create dictionary of all non-null properties on source attribute.
            Dictionary <string, PropertyInfo> nonNullProps = allProperties
                                                             .Where(prop => prop.GetValue(source) != null)
                                                             .ToDictionary(prop => prop.Name, prop => prop, StringComparer.OrdinalIgnoreCase);

            // Pick the ctor with the longest parameter list where all are matched to non-null props.
            var ctorAndParams = attributeType.GetConstructors(Flags)
                                .Select(ctor => new { ctor = ctor, parameters = ctor.GetParameters() })
                                .OrderByDescending(tuple => tuple.parameters.Length)
                                .FirstOrDefault(tuple => tuple.parameters.All(param => nonNullProps.ContainsKey(param.Name)));

            if (ctorAndParams == null)
            {
                throw new InvalidOperationException("Can't figure out which ctor to call.");
            }

            _matchedCtor = ctorAndParams.ctor;

            // Get appropriate binding data resolver (appsetting, autoresolve, or originalValue) for each constructor parameter
            _ctorParamResolvers = ctorAndParams.parameters
                                  .Select(param => GetResolver(nonNullProps[param.Name], nameResolver, bindingDataContract))
                                  .ToArray();

            // Get appropriate binding data resolver (appsetting, autoresolve, or originalValue) for each writeable property
            _propertySetters = allProperties
                               .Where(prop => prop.CanWrite)
                               .Select(prop =>
            {
                var resolver = GetResolver(prop, nameResolver, bindingDataContract);
                return((Action <TAttribute, BindingData>)((attr, data) => prop.SetValue(attr, resolver(attr, data))));
            })
                               .ToArray();
        }
        // transforms binding data to appropriate resolver (appsetting, autoresolve, or originalValue)
        private BindingDataResolver GetResolver(PropertyInfo propInfo, INameResolver nameResolver, BindingDataContract contract)
        {
            object originalValue = propInfo.GetValue(_source);
            AppSettingAttribute  appSettingAttr  = propInfo.GetCustomAttribute <AppSettingAttribute>();
            AutoResolveAttribute autoResolveAttr = propInfo.GetCustomAttribute <AutoResolveAttribute>();

            if (appSettingAttr != null && autoResolveAttr != null)
            {
                throw new InvalidOperationException($"Property '{propInfo.Name}' cannot be annotated with both AppSetting and AutoResolve.");
            }

            // first try to resolve with app setting
            if (appSettingAttr != null)
            {
                return(GetAppSettingResolver((string)originalValue, appSettingAttr, nameResolver, propInfo));
            }
            // try to resolve with auto resolve ({...}, %...%)
            if (autoResolveAttr != null && originalValue != null)
            {
                _autoResolves[propInfo] = autoResolveAttr;
                return(GetTemplateResolver((string)originalValue, autoResolveAttr, nameResolver, propInfo, contract));
            }
            // resolve the original value
            return((newAttr, bindingData) => originalValue);
        }
        // AutoResolve
        internal static BindingDataResolver GetTemplateResolver(string originalValue, AutoResolveAttribute attr, INameResolver nameResolver, PropertyInfo propInfo, BindingDataContract contract)
        {
            string            resolvedValue = nameResolver.ResolveWholeString(originalValue);
            var               template      = BindingTemplate.FromString(resolvedValue);
            IResolutionPolicy policy        = GetPolicy(attr.ResolutionPolicyType, propInfo);

            template.ValidateContractCompatibility(contract);
            return((newAttr, bindingData) => TemplateBind(policy, propInfo, newAttr, template, bindingData));
        }