diff --git a/src/Autofac/Util/Cache/TypeAssemblyReferenceProvider.cs b/src/Autofac/Util/Cache/TypeAssemblyReferenceProvider.cs index 0d56cf39c..c85e30902 100644 --- a/src/Autofac/Util/Cache/TypeAssemblyReferenceProvider.cs +++ b/src/Autofac/Util/Cache/TypeAssemblyReferenceProvider.cs @@ -39,14 +39,15 @@ public static IEnumerable GetAllReferencedAssemblies(MemberInfo member public static IEnumerable GetAllReferencedAssemblies(MemberInfo memberInfo, HashSet holdingSet) { holdingSet.Clear(); + var activeWorkingSet = new HashSet(); if (memberInfo is Type keyType) { - PopulateAllReferencedAssemblies(keyType, holdingSet); + PopulateAllReferencedAssemblies(keyType, activeWorkingSet, holdingSet); } else if (memberInfo.DeclaringType is Type declaredType) { - PopulateAllReferencedAssemblies(declaredType, holdingSet); + PopulateAllReferencedAssemblies(declaredType, activeWorkingSet, holdingSet); } return holdingSet; @@ -56,26 +57,33 @@ public static IEnumerable GetAllReferencedAssemblies(MemberInfo member /// Add to a provided all assemblies referenced by a given type. /// /// The type to retrieve references for. + /// A set to track types which have been processed. /// A set to add any assemblies to. - private static void PopulateAllReferencedAssemblies(Type inputType, HashSet holdingSet) + private static void PopulateAllReferencedAssemblies(Type inputType, HashSet activeWorkingSet, HashSet holdingSet) { if (inputType.IsArray && inputType.GetElementType() is Type elementType) { - PopulateAllReferencedAssemblies(elementType, holdingSet); + PopulateAllReferencedAssemblies(elementType, activeWorkingSet, holdingSet); } var genericArguments = inputType.GenericTypeArguments; foreach (var genericArgumentType in genericArguments) { - PopulateAllReferencedAssemblies(genericArgumentType, holdingSet); + if (activeWorkingSet.Contains(genericArgumentType)) + { + continue; + } + + PopulateAllReferencedAssemblies(genericArgumentType, activeWorkingSet, holdingSet); } holdingSet.Add(inputType.Assembly); + activeWorkingSet.Add(inputType); if (inputType.BaseType is not null && inputType.BaseType != typeof(object)) { - PopulateAllReferencedAssemblies(inputType.BaseType, holdingSet); + PopulateAllReferencedAssemblies(inputType.BaseType, activeWorkingSet, holdingSet); } } } diff --git a/test/Autofac.Test/Util/Cache/TypeAssemblyReferenceProviderTests.cs b/test/Autofac.Test/Util/Cache/TypeAssemblyReferenceProviderTests.cs index a8cfaf6ea..1c6bf6432 100644 --- a/test/Autofac.Test/Util/Cache/TypeAssemblyReferenceProviderTests.cs +++ b/test/Autofac.Test/Util/Cache/TypeAssemblyReferenceProviderTests.cs @@ -18,6 +18,7 @@ public class TypeAssemblyReferenceProviderTests [InlineData(typeof(IEnumerable>), new[] { typeof(IEnumerable<>), typeof(IIndex<,>), typeof(Assert) })] [InlineData(typeof(DerivedClass), new[] { typeof(DerivedClass), typeof(RegistrationBuilder<,,>), typeof(Assert) })] [InlineData(typeof(GenericDerivedClass), new[] { typeof(DerivedClass), typeof(RegistrationBuilder<,,>), typeof(Assert), typeof(object) })] + [InlineData(typeof(DerivedClassFromGenericAbstract), new[] { typeof(DerivedClassFromGenericAbstract) })] public void TypeReferencesCanBeDetermined(Type inputType, Type[] expandedTypeAssemblies) { Assert.NotNull(expandedTypeAssemblies); @@ -50,6 +51,15 @@ public void MemberInfoReferencesCanBeDetermined() } } + private abstract class GenericAbstractClass + where T : class + { + } + + private class DerivedClassFromGenericAbstract : GenericAbstractClass + { + } + private class DerivedClass : RegistrationBuilder {