/// <summary>
        /// Gets associate instance for specified parameters and result types.
        /// All associate created instances are cached and returned on the same calls further.
        /// </summary>
        /// <typeparam name="TKey1">First type to try to provide the associate for.</typeparam>
        /// <typeparam name="TKey2">Second type to try to provide the associate for.</typeparam>
        /// <typeparam name="TAssociate">Type of result to provide the associate for.</typeparam>
        /// <typeparam name="TResult">The type of result.</typeparam>
        /// <returns>Associate instance, if found;
        /// otherwise, <see langword="null"/>.</returns>
        /// <exception cref="InvalidOperationException"><c>InvalidOperationException</c>.</exception>
        protected TResult GetAssociate <TKey1, TKey2, TAssociate, TResult>()
            where TAssociate : class
        {
            var key = new TypePair(typeof(TKey1), typeof(TResult));

            return((TResult)cache.GetValue(key, _key => {
                Type foundFor;
                TAssociate associate1 = CreateAssociate <TKey1, TAssociate>(out foundFor);
                TAssociate associate2 = CreateAssociate <TKey2, TAssociate>(out foundFor);
                // Preferring non-null ;)
                TAssociate associate;
                if (associate1 == null)
                {
                    associate = associate2;
                }
                else if (associate2 == null)
                {
                    associate = associate1;
                }
                else
                {
                    // Both are non-null; preferring one of two
                    associate = PreferAssociate <TKey1, TKey2, TAssociate>(associate1, associate2);
                }
                if (associate == null)
                {
                    // Try to get complex associate (create it manually)
                    associate = CreateCustomAssociate <TKey1, TKey2, TAssociate>();
                    if (associate == null)
                    {
                        var stringBuilder = new StringBuilder();
                        for (int i = 0; i < TypeSuffixes.Length; i++)
                        {
                            if (i != 0)
                            {
                                stringBuilder.Append(", ");
                            }
                            stringBuilder.Append(TypeSuffixes[i]);
                        }
                        throw new InvalidOperationException(string.Format(
                                                                Strings.ExCantFindAssociate2, stringBuilder,
                                                                typeof(TAssociate).GetShortName(),
                                                                typeof(TKey1).GetShortName(),
                                                                typeof(TKey2).GetShortName()));
                    }
                }
                return ConvertAssociate <TKey1, TKey2, TAssociate, TResult>(associate);
            }));
        }
        /// <inheritdoc/>
        protected override TAssociate CreateCustomAssociate <TKey1, TKey2, TAssociate>()
        {
            Pair <Type> keyTypePair = new Pair <Type>(typeof(TKey1), typeof(TKey2));

            if (InProgress.ContainsKey(keyTypePair))
            {
                throw new InvalidOperationException(Strings.ExRecursiveAssociateLookupDetected);
            }
            InProgress.Add(keyTypePair, true);
            try {
                TAssociate associate = base.CreateCustomAssociate <TKey1, TKey2, TAssociate>();
                if (associate != null)
                {
                    return(associate);
                }
                IAdvancedConverterFactory <TKey1> f1 = base.GetAssociate <TKey1, IAdvancedConverterFactory <TKey1>, IAdvancedConverterFactory <TKey1> >();
                if (f1 != null)
                {
                    associate = f1.CreateForwardConverter <TKey2>() as TAssociate;
                    if (associate != null)
                    {
                        return(associate);
                    }
                }
                IAdvancedConverterFactory <TKey2> f2 = base.GetAssociate <TKey2, IAdvancedConverterFactory <TKey2>, IAdvancedConverterFactory <TKey2> >();
                if (f2 != null)
                {
                    associate = f2.CreateBackwardConverter <TKey1>() as TAssociate;
                    if (associate != null)
                    {
                        return(associate);
                    }
                }
                return(null);
            }
            finally {
                InProgress.Remove(keyTypePair);
            }
        }