diff --git a/src/GraphQL.Conventions/Types/Resolution/Extensions/ReflectionExtensions.cs b/src/GraphQL.Conventions/Types/Resolution/Extensions/ReflectionExtensions.cs index 394282d..0207af0 100644 --- a/src/GraphQL.Conventions/Types/Resolution/Extensions/ReflectionExtensions.cs +++ b/src/GraphQL.Conventions/Types/Resolution/Extensions/ReflectionExtensions.cs @@ -30,16 +30,19 @@ public static bool IsNullableType(this TypeInfo type) return type.IsGenericType(typeof(Nullable<>)); } - public static Type GetImplementInterface(this Type type, Type interfaceType, bool fuseGeneric = true) + public static Type GetImplementationInterface(this Type type, Type interfaceType, bool fuseGeneric = true) { if (!interfaceType.IsInterface) return null; + fuseGeneric &= interfaceType.IsGenericType; if (type.IsGenericType && fuseGeneric ? type.GetGenericTypeDefinition() == interfaceType.GetGenericTypeDefinition() - : type == interfaceType - ) + : type == interfaceType) + { return interfaceType; + } + while (type is not null) { var interfaces = type.GetInterfaces(); @@ -52,7 +55,7 @@ public static Type GetImplementInterface(this Type type, Type interfaceType, boo var @interface = interfaces[i]; if (mayFusedGenericInterface[i] == interfaceType) return interfaces[i]; - var ni = @interface.GetImplementInterface(interfaceType, fuseGeneric); + var ni = @interface.GetImplementationInterface(interfaceType, fuseGeneric); if (ni is not null) return ni; } @@ -63,8 +66,8 @@ public static Type GetImplementInterface(this Type type, Type interfaceType, boo return null; } - public static bool ImplementInterface(this Type type, Type interfaceType, bool fuseGeneric = true) => - type.GetImplementInterface(interfaceType, fuseGeneric) is not null; + public static bool IsImplementingInterface(this Type type, Type interfaceType, bool fuseGeneric = true) => + type.GetImplementationInterface(interfaceType, fuseGeneric) is not null; public static TypeInfo BaseType(this TypeInfo type) @@ -91,8 +94,8 @@ public static TypeInfo TypeParameter(this GraphTypeInfo type) } public static TypeInfo TypeParameterCollection(this TypeInfo type) => ( - type.GetImplementInterface(typeof(ICollection<>)) ?? - type.GetImplementInterface(typeof(IReadOnlyList<>)) + type.GetImplementationInterface(typeof(ICollection<>)) ?? + type.GetImplementationInterface(typeof(IReadOnlyList<>)) )?.GetTypeInfo(); public static bool IsPrimitiveGraphType(this TypeInfo type) @@ -105,12 +108,13 @@ public static bool IsPrimitiveGraphType(this TypeInfo type) public static bool IsEnumerableGraphType(this TypeInfo type) { - if (type.ImplementInterface(typeof(IDictionary)) || type.ImplementInterface(typeof(IDictionary<,>))) + if (type.IsImplementingInterface(typeof(IDictionary)) || type.IsImplementingInterface(typeof(IDictionary<,>))) { return false; } - return type.ImplementInterface(typeof(ICollection<>)) || - type.ImplementInterface(typeof(IReadOnlyCollection<>)) || + + return type.IsImplementingInterface(typeof(ICollection<>)) || + type.IsImplementingInterface(typeof(IReadOnlyCollection<>)) || type.IsGenericType(typeof(IEnumerable<>)) || (type.IsGenericType && type.DeclaringType == typeof(Enumerable)) || // Handles internal Iterator implementations for LINQ; for reference https://referencesource.microsoft.com/#system.core/System/Linq/Enumerable.cs type.IsArray; diff --git a/test/GraphQL.Conventions.Tests/Types/Resolution/Extensions/ReflectionExtensionsTests.cs b/test/GraphQL.Conventions.Tests/Types/Resolution/Extensions/ReflectionExtensionsTests.cs index 186201f..dd9b32a 100644 --- a/test/GraphQL.Conventions.Tests/Types/Resolution/Extensions/ReflectionExtensionsTests.cs +++ b/test/GraphQL.Conventions.Tests/Types/Resolution/Extensions/ReflectionExtensionsTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; @@ -10,13 +11,13 @@ namespace Tests.Types.Resolution.Extensions public class ReflectionExtensionsTests { [Theory] - [MemberData(nameof(IsEnumerableGraphType_Should_ReturnTrue_For_Common_Collection_Types__Data))] - public void IsEnumerableGraphType_Should_ReturnTrue_For_Common_Collection_Types(Type type) + [MemberData(nameof(IsEnumerableGraphType_Should_Return_True_For_Common_Collection_Types_Data))] + public void IsEnumerableGraphType_Should_Return_True_For_Common_Collection_Types(Type type) { Assert.IsTrue(type.GetTypeInfo().IsEnumerableGraphType()); } - public static TheoryData IsEnumerableGraphType_Should_ReturnTrue_For_Common_Collection_Types__Data() => new() + public static TheoryData IsEnumerableGraphType_Should_Return_True_For_Common_Collection_Types_Data() => new() { typeof(IEnumerable<>), typeof(ConcurrentQueue<>), @@ -25,15 +26,28 @@ public void IsEnumerableGraphType_Should_ReturnTrue_For_Common_Collection_Types( typeof(List<>), typeof(IList<>), typeof(IReadOnlyList<>), - typeof(IReadOnlyCollection<>) + typeof(IReadOnlyCollection<>), }; [Theory] - [MemberData(nameof(GetImplementInterface_WithoutFuse_AcquireSpecifyInterfaceOnly__Data))] - public void GetImplementInterface_WithoutFuse_AcquireSpecifyInterfaceOnly(Type type, Type assignableInterface) - => Assert.IsTrue(type.ImplementInterface(assignableInterface, false)); + [MemberData(nameof(IsEnumerableGraphType_Should_Return_False_For_Common_Dictionary_Types_Data))] + public void IsEnumerableGraphType_Should_Return_False_For_Common_Dictionary_Types(Type type) + { + Assert.IsFalse(type.GetTypeInfo().IsEnumerableGraphType()); + } + + public static TheoryData IsEnumerableGraphType_Should_Return_False_For_Common_Dictionary_Types_Data() => new() + { + typeof(IDictionary<,>), + typeof(IDictionary), + }; + + [Theory] + [MemberData(nameof(GetImplementationInterface_WithoutFuse_AcquireSpecifiedInterfaceOnly_Data))] + public void GetImplementationInterface_WithoutFuse_AcquireSpecifiedInterfaceOnly(Type type, Type assignableInterface) + => Assert.IsTrue(type.IsImplementingInterface(assignableInterface, false)); - public static TheoryData GetImplementInterface_WithoutFuse_AcquireSpecifyInterfaceOnly__Data() => + public static TheoryData GetImplementationInterface_WithoutFuse_AcquireSpecifiedInterfaceOnly_Data() => new() { { typeof(ITestInterface1), typeof(ITestInterface1) }, @@ -48,11 +62,11 @@ public static TheoryData GetImplementInterface_WithoutFuse_AcquireSp }; [Theory] - [MemberData(nameof(GetImplementInterface_WithFuse_AcquireSpecifyInterfaceOnly__Data))] - public void GetImplementInterface_WithFuse_AcquireSpecifyInterfaceOnly(Type type, Type assignableInterface) => - Assert.IsTrue(type.ImplementInterface(assignableInterface)); + [MemberData(nameof(GetImplementationInterface_WithFuse_AcquireSpecifiedInterfaceOnly_Data))] + public void GetImplementationInterface_WithFuse_AcquireSpecifiedInterfaceOnly(Type type, Type assignableInterface) => + Assert.IsTrue(type.IsImplementingInterface(assignableInterface)); - public static TheoryData GetImplementInterface_WithFuse_AcquireSpecifyInterfaceOnly__Data() => + public static TheoryData GetImplementationInterface_WithFuse_AcquireSpecifiedInterfaceOnly_Data() => new() { { typeof(ITestInterface1), typeof(ITestInterface1<>) },