private IMemberConfiguration <T, TN> RegisterInternal <T, TN>(bool memberCaseInsensitive = true)
        {
            lock (_lock)
            {
                var src      = typeof(T);
                var dest     = typeof(TN);
                var cacheKey = CalculateCacheKey(src, dest);

                if (SourceService.TypeMappers.ContainsKey(cacheKey) &&
                    DestinationService.TypeMappers.ContainsKey(cacheKey))
                {
                    var typeMapper = SourceService.TypeMappers[cacheKey] as ITypeMapper <T, TN>;
                    return(typeMapper?.MemberConfiguration);
                }

                if (src.GetInfo().GetInterfaces().Any(t => t.Name.Contains(typeof(IEnumerable).Name)) &&
                    dest.GetInfo().GetInterfaces().Any(t => t.Name.Contains(typeof(IEnumerable).Name)))
                {
                    throw new InvalidOperationException(
                              $"It is invalid to register mapping for collection types from {src.FullName} to {dest.FullName}, please use just class registration mapping and your collections will be implicitly processed. In case you want to include some custom collection mapping please use: Mapper.RegisterCustom.");
                }

                var sourceClassMapper      = new SourceTypeMapper <T, TN>(SourceService, this);
                var destinationClassMapper = new DestinationTypeMapper <T, TN>(DestinationService, this);

                SourceService.TypeMappers[cacheKey]      = sourceClassMapper;
                DestinationService.TypeMappers[cacheKey] = destinationClassMapper;
                var memberConfiguration = new MemberConfiguration <T, TN>(
                    new ITypeMapper <T, TN>[] { sourceClassMapper, destinationClassMapper }, this);
                sourceClassMapper.MemberConfiguration      = memberConfiguration;
                destinationClassMapper.MemberConfiguration = memberConfiguration;

                if (!CustomMappingsBySource.ContainsKey(src.GetHashCode()))
                {
                    CustomMappingsBySource[src.GetHashCode()] = new List <long>();
                }
                if (!CustomMappingsBySource[src.GetHashCode()].Contains(cacheKey))
                {
                    CustomMappingsBySource[src.GetHashCode()].Add(cacheKey);
                }

                return(memberConfiguration);
            }
        }
        private object MapNonGenericInternal(Type srcType, Type dstType, object src, object dest = null, bool dynamicTrial = false)
        {
            if (src == null)
            {
                return(null);
            }

            var cacheKey = CalculateCacheKey(srcType, dstType);

            if (CustomMappers.ContainsKey(cacheKey))
            {
                var customTypeMapper = CustomMappers[cacheKey];
                var typeMapper       = customTypeMapper();
                if (!_customTypeMapperCache.ContainsKey(cacheKey))
                {
                    CompileNonGenericCustomTypeMapper(srcType, dstType, typeMapper, cacheKey);
                }
                return(_customTypeMapperCache[cacheKey](src, dest));
            }

            ITypeMapper mapper        = null;
            var         actualSrcType = src.GetType();

            if (srcType != actualSrcType && actualSrcType.GetInfo().IsAssignableFrom(srcType))
            {
                throw new InvalidCastException($"Your source object instance type '{actualSrcType.FullName}' is not assignable from source type you specified '{srcType}'.");
            }

            var srcHash = actualSrcType.GetHashCode();

            if (dest != null)
            {
                var actualDstType = dest.GetType();
                if (dstType != actualDstType && actualDstType.GetInfo().IsAssignableFrom(dstType))
                {
                    throw new InvalidCastException($"Your destination object instance type '{actualSrcType.FullName}' is not assignable from destination type you specified '{srcType}'.");
                }

                if (CustomMappingsBySource.ContainsKey(srcHash))
                {
                    var mappings = CustomMappingsBySource[srcHash];

                    mapper =
                        mappings.Where(m => DestinationService.TypeMappers.ContainsKey(m))
                        .Select(m => DestinationService.TypeMappers[m])
                        .FirstOrDefault(tm => tm.DestinationType == actualDstType);
                }
            }
            else
            {
                if (CustomMappingsBySource.ContainsKey(srcHash))
                {
                    var mappings    = CustomMappingsBySource[srcHash];
                    var typeMappers =
                        mappings.Where(m => SourceService.TypeMappers.ContainsKey(m))
                        .Select(m => SourceService.TypeMappers[m])
                        .Where(m => dstType.GetInfo().IsAssignableFrom(m.DestinationType))
                        .ToList();
                    if (typeMappers.Count > 1)
                    {
                        if (typeMappers.All(tm => tm.DestinationType != dstType))
                        {
                            throw new AmbiguousMatchException(
                                      $"Source '{actualSrcType.FullName}' has more than one destination types' mappings");
                        }
                        mapper = typeMappers.FirstOrDefault(tm => tm.DestinationType == dstType);
                    }
                    else
                    {
                        mapper = typeMappers.First();
                    }
                }
            }

            var mappingService = dest == null ? SourceService : DestinationService;

            if (mapper != null)
            {
                var nonGenericMapFunc = mapper.GetNonGenericMapFunc();
                return(nonGenericMapFunc(src, dest));
            }

            if (mappingService.TypeMappers.ContainsKey(cacheKey))
            {
                mapper = mappingService.TypeMappers[cacheKey];
                var nonGenericMapFunc = mapper.GetNonGenericMapFunc();
                return(nonGenericMapFunc(src, dest));
            }

            var tCol =
                srcType.GetInfo().GetInterfaces()
                .FirstOrDefault(t => t.GetInfo().IsGenericType&& t.GetGenericTypeDefinition() == GenericEnumerableType) ??
                (srcType.GetInfo().IsGenericType &&
                 srcType.GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable))
                    ? srcType
                    : null);

            var tnCol = dstType.GetInfo().GetInterfaces()
                        .FirstOrDefault(t => t.GetInfo().IsGenericType&& t.GetGenericTypeDefinition() == GenericEnumerableType) ??
                        (dstType.GetInfo().IsGenericType&& dstType.GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable))
                            ? dstType
                            : null);

            if (tCol != null && tnCol != null)
            {
                CompileNonGenericCollectionMapping(srcType, dstType);
                // todo: make same signature in both compiled funcs with destination
                var result = (dest == null
                    ? _mappingServices.First(m => !m.DestinationSupport).MapCollection(cacheKey).DynamicInvoke(src)
                    : _mappingServices.First(m => m.DestinationSupport).MapCollection(cacheKey).DynamicInvoke(src, dest));
                return(result);
            }

            if (dynamicTrial)
            {
                throw new MapNotImplementedException(
                          $"There is no mapping has been found. Source Type: {srcType.FullName}, Destination Type: {dstType.FullName}");
            }
            dynamic source      = src;
            dynamic destination = dest;

            Register(source, destination);
            return(MapNonGenericInternal(srcType, dstType, src, dest, true));
        }