<PackageReference Include="NUnit" Version="4.3.0" />

GenericMethodHelper

public class GenericMethodHelper
GenericMethodHelper is able to deduce the Type arguments for a generic method from the actual arguments provided.
using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; namespace NUnit.Framework.Internal { [NullableContext(1)] [Nullable(0)] public class GenericMethodHelper { [NullableContext(0)] private static class ConflictingTypesMarkerClass { } private static readonly Type ConflictingTypesMarker = typeof(ConflictingTypesMarkerClass); private MethodInfo Method { get; } private Type[] TypeParms { get; } private Type[] TypeArgs { get; } private Type[] ParmTypes { get; } public GenericMethodHelper(MethodInfo method) { Guard.ArgumentValid(method.IsGenericMethod, "Specified method must be generic", "method"); Method = method; TypeParms = Method.GetGenericArguments(); TypeArgs = new Type[TypeParms.Length]; ParameterInfo[] parameters = Method.GetParameters(); ParmTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { ParmTypes[i] = parameters[i].ParameterType; } } public bool TryGetTypeArguments([Nullable(new byte[] { 1, 2 })] object[] argList, [Nullable(new byte[] { 2, 1 })] [NotNullWhen(true)] out Type[] typeArguments) { Guard.ArgumentValid(argList.Length == ParmTypes.Length, "Supplied arguments do not match required method parameters", "argList"); for (int i = 0; i < ParmTypes.Length; i++) { object obj = argList[i]; if (obj != null) { Type type = obj.GetType(); TryApplyArgType(ParmTypes[i], type); } } Type[] typeArgs = TypeArgs; foreach (Type type2 in typeArgs) { if ((object)type2 == null || type2 == ConflictingTypesMarker) { typeArguments = null; return false; } } typeArguments = TypeArgs; return true; } private void TryApplyArgType(Type parmType, Type argType) { if (parmType.IsGenericParameter) ApplyArgType(parmType, argType); else if (parmType.ContainsGenericParameters) { Type[] array = (!parmType.IsArray) ? parmType.GetGenericArguments() : new Type[1] { parmType.GetElementType() }; if (argType.HasElementType) ApplyArgType(array[0], argType.GetElementType()); else if (argType.IsGenericType && IsAssignableToGenericType(argType, parmType)) { Type[] genericArguments = argType.GetGenericArguments(); if (genericArguments.Length == array.Length) { for (int i = 0; i < array.Length; i++) { TryApplyArgType(array[i], genericArguments[i]); } } } else if (Reflect.IsNullable(parmType) && !argType.IsClass) { ApplyArgType(array[0], argType); } } } private void ApplyArgType(Type parmType, Type argType) { int genericParameterPosition = parmType.GenericParameterPosition; if (!TypeHelper.TryGetBestCommonType(TypeArgs[genericParameterPosition], argType, out TypeArgs[genericParameterPosition])) TypeArgs[genericParameterPosition] = ConflictingTypesMarker; } private bool IsAssignableToGenericType(Type givenType, Type genericType) { Type[] interfaces = givenType.GetInterfaces(); Type[] array = interfaces; foreach (Type type in array) { if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition.Name == genericType.Name && genericTypeDefinition.Namespace == genericType.Namespace) return true; } } if (givenType.IsGenericType) { Type genericTypeDefinition2 = givenType.GetGenericTypeDefinition(); if (genericTypeDefinition2.Name == genericType.Name && genericTypeDefinition2.Namespace == genericType.Namespace) return true; } Type baseType = givenType.BaseType; if ((object)baseType == null) return false; return IsAssignableToGenericType(baseType, genericType); } } }