Scrutor
Scrutor copied to clipboard
ReflectionExtensions.HasMatchingGenericArity has a bug
When the implementation class is a generic class that is implementing a generic interface as below, it's not able to detect IEventHandler<CustomEvent>
and IEventHandler
from ActionableEventHandler<CustomEvent>
class CustomEvent { }
interface IEventHandler { }
interface IEventHandler<TEvent> : IEventHandler { }
class EventHandler<TEvent> : IEventHandler<TEvent> { }
class ActionableEventHandler<TEvent> : EventHandler<TEvent> { }
Possible fix can be:
public static bool HasMatchingGenericArity(this Type interfaceType, TypeInfo typeInfo) {
if (typeInfo.IsGenericType) {
var interfaceTypeInfo = interfaceType.GetTypeInfo();
if (interfaceTypeInfo.IsGenericType) {
var interfaceArgCount = interfaceType.GenericTypeArguments.Length;
var implementationArgCount = typeInfo.GenericTypeArguments.Length;
return interfaceArgCount == implementationArgCount;
}
// return false;
}
return true;
}
I am seeing the same issue. Basically if the concrete class has fewer generic parameters than the interfaces, the HasMatchingGenericArity
predicate fails and the interface is not added. This check is correct to do when trying to register open generics (and maybe partially closed generics?) but not fully closed generics.
var servicesA = new ServiceCollection();
servicesA.Scan(scan => scan.AddTypes(typeof(Service<int>))
.AsImplementedInterfaces()
.WithTransientLifetime());
// Will throw since nothing was added.
//var descriptorA = servicesA[0];
//Console.WriteLine($"ServiceType: {descriptorA.ServiceType}, ImplType: {descriptorA.ImplementationType}");
var servicesB = new ServiceCollection();
servicesB.Scan(scan => scan.AddTypes(typeof(Service<int>))
.As(static type => type.GetInterfaces()) // Works without the HasMatchingGenericArity check
.WithTransientLifetime());
var descriptorB = servicesB[0];
Console.WriteLine($"ServiceType: {descriptorB.ServiceType}, ImplType: {descriptorB.ImplementationType}");
public interface IServiceA<T1,T2>{}
public class Service<T> : IServiceA<string,T> {}