使用反射来区分泛型方法和泛型参数

时间:2017-11-13 17:01:37

标签: c# generics reflection arguments

在EntityFrameworkQueryableExtensions中有两个方法,都称为ThenInclude,具有以下签名:

public static IIncludableQueryable<TEntity, TProperty> ThenInclude<TEntity, TPreviousProperty, TProperty>(this IIncludableQueryable<TEntity, TPreviousProperty> source, Expression<Func<TPreviousProperty, TProperty>> navigationPropertyPath)where TEntity : class

public static IIncludableQueryable<TEntity, TProperty> ThenInclude<TEntity, TPreviousProperty, TProperty>(this IIncludableQueryable<TEntity, IEnumerable<TPreviousProperty>> source, Expression<Func<TPreviousProperty, TProperty>> navigationPropertyPath)where TEntity : class

不同之处在于第二个签名具有IEnumerable&lt; TPreviousProperty&gt;在'this'参数的类型中,而第一个签名只有TPreviousProperty。

问题是,如何使用反射和MakeGenericMethod获取第二个(或第一个)的MethodInfo?

到目前为止,我所能想到的就是添加一个额外的图层:

class Whatever<TEntity> where TEntity: class {
    private static MethodInfo ThenIncludeEnumerableMethod<TPreviousProperty,TProperty>()
    {
        Func<IIncludableQueryable<TEntity,IEnumerable<TPreviousProperty>>, Expression<Func<TPreviousProperty, TProperty>>, IIncludableQueryable<TEntity, TProperty>> thenIncludeLambda = (source, lambda) => source.ThenInclude(lambda);
        return thenIncludeLambda.Method;
    }
}

应该有更直接的方法来做到这一点。

注意Reflection: How to get a generic method?不是这个问题的答案。

1 个答案:

答案 0 :(得分:0)

您想致电

typeof(EntityFrameworkQueryableExtensions).GetMethod("ThenInclude", new [] { type1, type2 })

但是你不能这样做,因为类型参数type1type2是由MethodInfo.GetGenericArguments()为两个名为&#34; ThenInclude&#34;的方法返回的开放泛型类型构造的。

你可以做的是循环遍历名为&#34; ThenInclude&#34;的所有方法。具有所需数量的泛型参数和参数,将打开的泛型参数映射到所需的参数类型,并检查实际参数类型是否与所需类型匹配:

class Whatever<TEntity> where TEntity : class
{
    public static MethodInfo ThenIncludeMethod<TPreviousProperty, TProperty>()
    {
        var query = from m in typeof(EntityFrameworkQueryableExtensions).GetMethods()
                    where m.Name == "ThenInclude" && m.IsGenericMethodDefinition
                    let args = m.GetGenericArguments()
                    where args.Length == 3
                    let tEntityType = args[0]
                    let tPreviousPropertyType = args[1]
                    let tPropertyType = args[2]
                    let parameters = m.GetParameters()
                    where parameters.Length == 2
                    where parameters[0].ParameterType == typeof(IIncludableQueryable<,>).MakeGenericType(new Type[] { tEntityType, tPreviousPropertyType })
                    where parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(new[] { tPreviousPropertyType, tPropertyType }))
                    select m.MakeGenericMethod(new[] { typeof(TEntity), typeof(TPreviousProperty), typeof(TProperty) });
        return query.SingleOrDefault();
    }

    public static MethodInfo ThenIncludeEnumerableMethod<TPreviousProperty, TProperty>()
    {
        var query = from m in typeof(EntityFrameworkQueryableExtensions).GetMethods()
                    where m.Name == "ThenInclude" && m.IsGenericMethodDefinition
                    let args = m.GetGenericArguments()
                    where args.Length == 3
                    let tEntityType = args[0]
                    let tPreviousPropertyType = args[1]
                    let tPropertyType = args[2]
                    let parameters = m.GetParameters()
                    where parameters.Length == 2
                    where parameters[0].ParameterType == typeof(IIncludableQueryable<,>).MakeGenericType(new Type[] { tEntityType, typeof(IEnumerable<>).MakeGenericType(new[] { tPreviousPropertyType }) })
                    where parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(new[] { tPreviousPropertyType, tPropertyType }))
                    select m.MakeGenericMethod(new[] { typeof(TEntity), typeof(TPreviousProperty), typeof(TProperty) });
        return query.SingleOrDefault();
    }
}

老实说,你当前的方法要简单得多,但它会返回MethodInfo声明的Whatever<T>声明EntityFrameworkQueryableExtensions.ThenInclude()的{​​{1}},同时返回MethodInfo直接EntityFrameworkQueryableExtensionsThenInclude<,,>()的具体方法。

注意我使用完整的.Net写了这个。如果您使用的是.Net Core,您可能需要按照System.Reflection.TypeExtensions描述here,然后我相信它应该有用。