diff --git a/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs b/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs index f195e8f01..e392c05a6 100644 --- a/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs +++ b/src/Autofac/Features/ResolveAnything/AnyConcreteTypeNotAlreadyRegisteredSource.cs @@ -4,6 +4,7 @@ using Autofac.Builder; using Autofac.Core; using Autofac.Core.Registration; +using Autofac.Util; namespace Autofac.Features.ResolveAnything; @@ -55,9 +56,7 @@ public IEnumerable RegistrationsFor( } var serviceType = ts.ServiceType; - if (!serviceType.IsClass || - serviceType.IsSubclassOf(typeof(Delegate)) || - serviceType.IsAbstract || + if (!serviceType.MayAllowReflectionActivation(allowCompilerGenerated: true) || serviceType.IsGenericTypeDefinition || !_predicate(ts.ServiceType) || registrationAccessor(service).Any()) diff --git a/src/Autofac/Features/Scanning/AssemblyExtensions.cs b/src/Autofac/Features/Scanning/AssemblyExtensions.cs index 3b37551f8..200fbe596 100644 --- a/src/Autofac/Features/Scanning/AssemblyExtensions.cs +++ b/src/Autofac/Features/Scanning/AssemblyExtensions.cs @@ -24,7 +24,7 @@ internal static IEnumerable GetPermittedTypesForAssemblyScanning(this Asse static IReadOnlyList Uncached(Assembly assembly) { return assembly.GetLoadableTypes() - .WhichCanBeRegistered() + .WhichAreAllowedThroughScanning() .ToList(); } diff --git a/src/Autofac/Features/Scanning/TypeExtensions.cs b/src/Autofac/Features/Scanning/TypeExtensions.cs index 9aab9109d..3443d019d 100644 --- a/src/Autofac/Features/Scanning/TypeExtensions.cs +++ b/src/Autofac/Features/Scanning/TypeExtensions.cs @@ -38,29 +38,13 @@ internal static IEnumerable AllowedByActivatorFilters /// A filtered set of types that remove non-concrete types (interfaces, abstract classes, etc.). /// - internal static IEnumerable WhichCanBeRegistered(this IEnumerable types) => types.Where(t => t.IsRegisterableType()); - - /// - /// Determines if a type is a concrete type that is allowed to be registered during scanning. - /// - /// - /// The type to check. - /// - /// - /// if the type is allowed to be registered during scanning based on its reflection attributes; otherwise . - /// - // Issue #897: For back compat reasons we can't filter out - // non-public types here. Folks use assembly scanning on their - // own stuff, so encapsulation is a tricky thing to manage. - // If people want only public types, a LINQ Where clause can be used. - // - // Run IsCompilerGenerated check last due to perf. See AssemblyScanningPerformanceTests.MeasurePerformance. - internal static bool IsRegisterableType(this Type? type) => type != null && type.IsClass && !type.IsAbstract && !type.IsDelegate() && !type.IsCompilerGenerated(); + internal static IEnumerable WhichAreAllowedThroughScanning(this IEnumerable types) => types.Where(t => t.MayAllowReflectionActivation()); /// /// Filters a list of types down to only those that are concrete - /// (), not open generic, and allowed by the - /// provided activator data. Registers those types into the provided + /// (), not open generic, + /// and allowed by the provided activator data. Registers those types into + /// the provided /// registry. /// /// @@ -75,7 +59,7 @@ internal static IEnumerable AllowedByActivatorFilters internal static void FilterAndRegisterConcreteTypes(this IEnumerable types, IComponentRegistryBuilder cr, IRegistrationBuilder rb) { - var closedTypes = types.WhichCanBeRegistered() + var closedTypes = types.WhichAreAllowedThroughScanning() .Where(t => !t.IsGenericTypeDefinition) .AllowedByActivatorFilters(rb.ActivatorData); diff --git a/src/Autofac/Util/InternalTypeExtensions.cs b/src/Autofac/Util/InternalTypeExtensions.cs index fa08a8683..41fc7bdfa 100644 --- a/src/Autofac/Util/InternalTypeExtensions.cs +++ b/src/Autofac/Util/InternalTypeExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Autofac Project. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. -using System.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; using Autofac.Core; @@ -149,7 +148,7 @@ static bool Uncached(Type type) } /// - /// Checks whether a given type is a generic list of colleciton interface type, e.g. , and the read-only variants. + /// Checks whether a given type is a generic list of collection interface type, e.g. , and the read-only variants. /// /// The type to check. /// True if the type is one of the supported list/collection types. @@ -168,7 +167,7 @@ static bool Uncached(Type type) } /// - /// Checks whether a given type is a closed generic defined by the specifed open generic. + /// Checks whether a given type is a closed generic defined by the specified open generic. /// /// The type to check. /// The open generic to check against. @@ -214,6 +213,33 @@ public static bool IsOpenGenericTypeOf(this Type @this, Type type) || @this.CheckInterfacesAreOpenGenericTypeOf(type); } + /// + /// Filters off interfaces, abstract types, and generally non-registerable + /// types. Largely used during type/assembly scanning, but also can + /// determine if this is something we can activate by reflection. + /// Intentionally does NOT filter out open generic definitions because this + /// gets used in both concrete and open generic scanning. + /// + /// + /// The type to check. + /// + /// + /// to allow compiler-generated types to be + /// considered registerable. Defaults to . + /// + /// + /// if the type is allowed to be registered during + /// scanning based on its reflection attributes; otherwise . + /// + // Issue #897: For back compat reasons we can't filter out + // non-public types here. Folks use assembly scanning on their + // own stuff, so encapsulation is a tricky thing to manage. + // If people want only public types, a LINQ Where clause can be used. + // + // Run IsCompilerGenerated check last due to perf. See AssemblyScanningPerformanceTests.MeasurePerformance. + internal static bool MayAllowReflectionActivation(this Type? type, bool allowCompilerGenerated = false) => type != null && type.IsClass && !type.IsAbstract && !type.IsDelegate() && (allowCompilerGenerated || !type.IsCompilerGenerated()); + private static bool CheckBaseTypeIsOpenGenericTypeOf(this Type @this, Type type) { if (@this.BaseType == null) @@ -287,7 +313,9 @@ private static bool ParameterCompatibleWithTypeConstraint(Type parameter, Type c .Any(p => ParameterEqualsConstraint(p, constraint)); } +#if NET6_0_OR_GREATER [SuppressMessage("Microsoft.Design", "CA1031", Justification = "Implementing a real TryMakeGenericType is not worth the effort.")] +#endif private static bool ParameterEqualsConstraint(Type parameter, Type constraint) { var genericArguments = parameter.GenericTypeArguments;