protected virtual List <MethodDefinition> GetFilteredInjecteeMethods(
            AssemblyDefinitionCachedData assemblyDefinitionCachedData,
            List <IgnoredMemberReference> ignoredMemberReferences)
        {
            Dictionary <string, Regex> regexCache = new Dictionary <string, Regex>();

            Log.DebugFormat("Number of types before filtering: {0}", assemblyDefinitionCachedData.AllTypes.Count);
            TypeDefinition[] types =
                assemblyDefinitionCachedData
                .AllTypes
                .Where(TestType)
                .ToArray();
            Log.DebugFormat("Number of types after filtering: {0}", types.Length);

            List <MethodDefinition> injecteeMethods =
                types
                .SelectMany(GetTestedMethodsFromType)
                .Where(ValidateInjecteeMethod)
                .ToList();

            Log.DebugFormat("Number of methods after filtering: {0}", injecteeMethods.Count);

            return(injecteeMethods);

            Regex GetFilterRegex(IgnoredMemberReference ignoredMemberReference)
            {
                if (!regexCache.TryGetValue(ignoredMemberReference.Filter, out Regex filterRegex))
                {
                    filterRegex = new Regex(ignoredMemberReference.Filter, RegexOptions.Compiled);
                    regexCache[ignoredMemberReference.Filter] = filterRegex;
                }

                return(filterRegex);
            }

            bool TestString(IgnoredMemberReference ignoredMemberReference, string fullName)
            {
                if (ignoredMemberReference.IsRegex)
                {
                    if (GetFilterRegex(ignoredMemberReference).IsMatch(fullName))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (fullName.Contains(ignoredMemberReference.Filter))
                    {
                        return(false);
                    }
                }

                return(true);
            }

            bool TestType(TypeDefinition type)
            {
                foreach (IgnoredMemberReference ignoredMemberReference in ignoredMemberReferences)
                {
                    if (!ignoredMemberReference.FilterFlags.HasFlag(IgnoredMemberReferenceFlags.SkipTypes))
                    {
                        continue;
                    }

                    if (!TestTypeIgnored(type, ignoredMemberReference))
                    {
                        return(false);
                    }
                }

                return(true);
            }

            bool TestTypeIgnored(TypeDefinition type, IgnoredMemberReference ignoredMemberReference)
            {
                while (true)
                {
                    if (!TestString(ignoredMemberReference, type.FullName))
                    {
                        Log.DebugFormat("Ignored type '{0}'", type.FullName);
                        return(false);
                    }

                    if (!ignoredMemberReference.MatchAncestors || type.BaseType == null)
                    {
                        break;
                    }

                    type = type.BaseType.GetDefinition();
                }
                return(true);
            }

            IEnumerable <MethodDefinition> GetTestedMethodsFromType(TypeDefinition type)
            {
                HashSet <MethodDefinition> ignoredMethods = new HashSet <MethodDefinition>();

                foreach (PropertyDefinition property in type.Properties)
                {
                    foreach (IgnoredMemberReference ignoredMemberReference in ignoredMemberReferences)
                    {
                        if (!ignoredMemberReference.FilterFlags.HasFlag(IgnoredMemberReferenceFlags.SkipProperties))
                        {
                            continue;
                        }

                        if (ProcessPropertyIgnored(property, ignoredMemberReference, ignoredMethods))
                        {
                            break;
                        }
                    }
                }

                foreach (MethodDefinition method in type.Methods)
                {
                    foreach (IgnoredMemberReference ignoredMemberReference in ignoredMemberReferences)
                    {
                        if (!ignoredMemberReference.FilterFlags.HasFlag(IgnoredMemberReferenceFlags.SkipMethods))
                        {
                            continue;
                        }

                        if (ProcessMethodIgnored(method, ignoredMemberReference, ignoredMethods))
                        {
                            break;
                        }
                    }
                }

                return(type.Methods.Except(ignoredMethods).Distinct());
            }

            bool ProcessPropertyIgnored(PropertyDefinition property, IgnoredMemberReference ignoredMemberReference, HashSet <MethodDefinition> ignoredMethods)
            {
                PropertyDefinition startProperty = property;

                while (true)
                {
                    if (!TestString(ignoredMemberReference, property.GetFullSimpleName()))
                    {
                        Log.DebugFormat("Ignored property '{0}'", startProperty.GetFullSimpleName());
                        if (startProperty.GetMethod != null)
                        {
                            ignoredMethods.Add(startProperty.GetMethod);
                        }
                        if (startProperty.SetMethod != null)
                        {
                            ignoredMethods.Add(startProperty.SetMethod);
                        }
                        return(false);
                    }

                    if (!ignoredMemberReference.MatchAncestors)
                    {
                        break;
                    }

                    PropertyDefinition baseProperty = property.GetBaseProperty();
                    if (baseProperty == property)
                    {
                        break;
                    }

                    property = baseProperty;
                }

                return(true);
            }

            bool ProcessMethodIgnored(MethodDefinition method, IgnoredMemberReference ignoredMemberReference, HashSet <MethodDefinition> ignoredMethods)
            {
                MethodDefinition startMethod = method;

                while (true)
                {
                    if (!TestString(ignoredMemberReference, method.GetFullSimpleName()))
                    {
                        ignoredMethods.Add(startMethod);
                        Log.DebugFormat("Ignored method '{0}'", startMethod.GetFullSimpleName());
                        return(false);
                    }

                    if (!ignoredMemberReference.MatchAncestors)
                    {
                        break;
                    }

                    MethodDefinition baseMethod = method.GetBaseMethod();
                    if (baseMethod == method)
                    {
                        break;
                    }

                    method = baseMethod;
                }

                return(true);
            }
        }