【问题标题】:Get a generic method without using GetMethods在不使用 GetMethods 的情况下获取泛型方法
【发布时间】:2008-11-06 17:42:20
【问题描述】:

我想得到方法System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector) 方法,但我一直想出空值。

var type = typeof(T);
var propertyInfo = type.GetProperty(group.PropertyName);
var propertyType = propertyInfo.PropertyType;

var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);
var expressionType = typeof(Expression<>).MakeGenericType(sorterType);

var queryType = typeof(IQueryable<T>);

var orderBy = typeof(System.Linq.Queryable).GetMethod("OrderBy", new[] { queryType, expressionType }); /// is always null.

有人有什么见解吗?我宁愿不循环通过 GetMethods 结果。

【问题讨论】:

标签: c# .net generics reflection


【解决方案1】:

已解决(通过破解 LINQ)!

我在研究同一问题时看到了您的问题。在没有找到好的解决方案后,我萌生了查看 LINQ 表达式树的想法。这是我想出的:

public static MethodInfo GetOrderByMethod<TElement, TSortKey>()
{
    Func<TElement, TSortKey> fakeKeySelector = element => default(TSortKey);

    Expression<Func<IEnumerable<TElement>, IOrderedEnumerable<TElement>>> lamda
        = list => list.OrderBy(fakeKeySelector);

    return (lamda.Body as MethodCallExpression).Method;
}

static void Main(string[] args)
{
    List<int> ints = new List<int>() { 9, 10, 3 };
    MethodInfo mi = GetOrderByMethod<int, string>();           
    Func<int,string> keySelector = i => i.ToString();
    IEnumerable<int> sortedList = mi.Invoke(null, new object[] { ints, 
                                                                 keySelector }
                                           ) as IEnumerable<int>;

    foreach (int i in sortedList)
    {
        Console.WriteLine(i);
    }
}

输出:10 3 9

编辑:如果您在编译时不知道类型,以下是获取方法的方法:

public static MethodInfo GetOrderByMethod(Type elementType, Type sortKeyType)
{
    MethodInfo mi = typeof(Program).GetMethod("GetOrderByMethod", Type.EmptyTypes);

    var getOrderByMethod = mi.MakeGenericMethod(new Type[] { elementType,
                                                             sortKeyType });
    return getOrderByMethod.Invoke(null, new object[] { }) as MethodInfo;
}

请务必将 typeof(Program) 替换为 typeof(WhateverClassYouDeclareTheseMethodsIn)。

【讨论】:

  • 您的替代方法不循环 Type.GetMethods 结果让我很好奇:它的性能如何?出乎我的意料,循环实际上是faster
  • @DamirArh,感谢您进行测试。这是违反直觉的。我仍然有点喜欢静态方法,因为它为 .NET 提供了更多的工作,并且可以利用未来的优化,如果它们发生的话。但是,对于性能关键的代码部分,最好检查一下。
  • @NeilWhitaker 干得好!我是新手,我想反映的是“Where”而不是“OrderBy”。你能写一个更常见/通用的吗?
【解决方案2】:

您的解决方案的变体,作为扩展方法:

public static class TypeExtensions
{
    private static readonly Func<MethodInfo, IEnumerable<Type>> ParameterTypeProjection = 
        method => method.GetParameters()
                        .Select(p => p.ParameterType.GetGenericTypeDefinition());

    public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes)
    {
        return (from method in type.GetMethods()
                where method.Name == name
                where parameterTypes.SequenceEqual(ParameterTypeProjection(method))
                select method).SingleOrDefault();
    }
}

【讨论】:

  • 有趣,谢谢,我需要吸收这个 SquenceEqual 方法。
  • 当参数的类型是泛型时,将什么作为参数传递给 parameterTypes?
  • @dudeNumber4:不清楚你的意思。我建议你用一个具体的例子提出一个新问题。
  • 我应该能够澄清:我有 MyClass.MyMethod( T gnrc )。如果我想调用上面的 GetGenericMethod 来获取 MyMethod 的 MethodInfo,我会将 typeof(MyClass) 作为第一个参数传递,“MyMethod”作为第二个参数,但是我将传递什么作为第三个参数?
  • @dudeNumber4:啊,我明白了。基本上你不会 - 你必须打电话给GetMethods 然后自己过滤。这是反射 API 的一个弱点。
【解决方案3】:

我不相信有一种简单的方法可以做到这一点 - 它基本上是反射中缺少的功能,IIRC。您必须遍历这些方法才能找到您想要的方法:(

【讨论】:

    【解决方案4】:

    我认为下面的扩展方法可以解决这个问题:

    public static MethodInfo GetGenericMethod(
      this Type type, string name, Type[] generic_type_args, Type[] param_types, bool complain = true)
    {
      foreach (MethodInfo m in type.GetMethods())
        if (m.Name == name)
        {
          ParameterInfo[] pa = m.GetParameters();
          if (pa.Length == param_types.Length)
          {
            MethodInfo c = m.MakeGenericMethod(generic_type_args);
            if (c.GetParameters().Select(p => p.ParameterType).SequenceEqual(param_types))
              return c;
          }
        }
      if (complain)
        throw new Exception("Could not find a method matching the signature " + type + "." + name +
          "<" + String.Join(", ", generic_type_args.AsEnumerable()) + ">" +
          "(" + String.Join(", ", param_types.AsEnumerable()) + ").");
      return null;
    }
    

    调用类似于(只是更改原始代码的最后一行):

    var type = typeof(T);  
    var propertyInfo = type.GetProperty(group.PropertyName);  
    var propertyType = propertyInfo.PropertyType;  
    
    var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);  
    var expressionType = typeof(Expression<>).MakeGenericType(sorterType);  
    
    var queryType = typeof(IQueryable<T>);  
    
    var orderBy = typeof(Queryable).GetGenericMethod("OrderBy",
                                                     new Type[] { type, propertyType },
                                                     new[] { queryType, expressionType });
    

    与其他解决方案的不同之处在于:生成的方法与参数类型完全匹配,而不仅仅是它们的通用基类型。

    【讨论】:

      【解决方案5】:

      今天有一个很好的替代方法Type.MakeGenericMethodParameter。以下 sn -p 检索Queryable.OrderBy 方法:

      var TSource = Type.MakeGenericMethodParameter(0);
      var TKey = Type.MakeGenericMethodParameter(1);
      var orderBy = typeof(Queryable).GetMethod(nameof(Queryable.OrderBy), 2, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard
          , new[] { typeof(IQueryable<>).MakeGenericType(TSource), typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(TSource, TKey)) }
          , null);
      Assert.NotNull(orderBy);
      

      【讨论】:

        【解决方案6】:
        var orderBy =
                (from methodInfo in typeof(System.Linq.Queryable).GetMethods()
                 where methodInfo.Name == "OrderBy"
                 let parameterInfo = methodInfo.GetParameters()
                 where parameterInfo.Length == 2
                 && parameterInfo[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
                 && parameterInfo[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)
                 select
                    methodInfo
                ).Single();
        

        【讨论】:

          【解决方案7】:

          如果您在编译时确实知道类型,则可以用更少的代码完成此操作,而无需使用 Expression 类型,或者完全依赖于 Linq,如下所示:

          public static MethodInfo GetOrderByMethod<TElement, TSortKey>() {
              IEnumerable<TElement> col = null;
              return new Func<Func<TElement, TSortKey>, IOrderedEnumerable<TElement>>(col.OrderBy).Method;
          }
          

          【讨论】:

          • 重要说明,这只适用于扩展方法,否则你会从 col.OrderBy 中得到 NullReferenceException,所以你最好使用 Enumerable.OrderBy 来显式地获取你的静态方法'再指。如果您想在没有非空实例的情况下获取实例方法,则需要使用例外答案中描述的 Linq.Expressions 方法。
          【解决方案8】:

          使用 lambda 表达式可以轻松获取泛型方法

              var method = type.GetGenericMethod
                      (c => c.Validate((IValidator<object>)this, o, action));
          

          在此处了解更多信息:

          http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html

          http://web.archive.org/web/20100911074123/http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html

          【讨论】:

          • 这太棒了,我整天都在努力寻找一种体面的方法来获得具有重载的泛型方法。我创建了一个稍微简化的版本,但你的想法帮助了我很多;)
          • 链接已过期。这看起来像new location
          【解决方案9】:

          我认为它可以用这样的类来制作:

          public static class SortingUtilities<T, TProperty>
          {
              public static IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
              {
                  return query.OrderBy(selector);
              }
          
          
              public static IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
              {
                  return query.OrderByDescending(selector);
              }
          
              public static IQueryable<T> Preload(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
              {
                  return query.Include(selector);
              }
          }
          

          你甚至可以像这样使用它:

          public class SortingOption<T> where T: class
          {
              private MethodInfo ascendingMethod;
              private MethodInfo descendingMethod;
              private LambdaExpression lambda;
              public string Name { get; private set; }
          
              public SortDirection DefaultDirection { get; private set; }
          
              public bool ApplyByDefault { get; private set; }
          
              public SortingOption(PropertyInfo targetProperty, SortableAttribute options)
              {
                  Name = targetProperty.Name;
                  DefaultDirection = options.Direction;
                  ApplyByDefault = options.IsDefault;
                  var utilitiesClass = typeof(SortingUtilities<,>).MakeGenericType(typeof(T), targetProperty.PropertyType);
                  ascendingMethod = utilitiesClass.GetMethod("ApplyOrderBy", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
                  descendingMethod = utilitiesClass.GetMethod("ApplyOrderByDescending", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
                  var param = Expression.Parameter(typeof(T));
                  var getter = Expression.MakeMemberAccess(param, targetProperty);
                  lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), targetProperty.PropertyType), getter, param);
              }
          
              public IQueryable<T> Apply(IQueryable<T> query, SortDirection? direction = null)
              {
                  var dir = direction.HasValue ? direction.Value : DefaultDirection;
                  var method = dir == SortDirection.Ascending ? ascendingMethod : descendingMethod;
                  return (IQueryable<T>)method.Invoke(null, new object[] { query, lambda });
              }
          }
          

          具有这样的属性:

          public class SortableAttribute : Attribute 
          {
              public SortDirection Direction { get; set; }
              public bool IsDefault { get; set; }
          }
          

          这个枚举:

          public enum SortDirection
          {
              Ascending,
              Descending
          }
          

          【讨论】:

            【解决方案10】:

            只是另一个评论(应该是,但由于它太长,我必须将其作为答案发布)跟进@NeilWhitaker -s 答案(这里使用 Enumerable.Count),因为我们正在清除串出来:) 为什么不在你的 bytype 方法中使用表达式树呢? 类似的东西:

                #region Count
                /// <summary>
                /// gets the 
                /// public static int Count&lt;TSource>(this IEnumerable&lt;TSource> source);
                /// methodinfo
                /// </summary>
                /// <typeparam name="TSource">type of the elements</typeparam>
                /// <returns></returns>
                public static MethodInfo GetCountMethod<TSource>()
                {
                    Expression<Func<IEnumerable<TSource>, int>> lamda = list => list.Count();
                    return (lamda.Body as MethodCallExpression).Method;
                }
            
                /// <summary>
                /// gets the 
                /// public static int Count&lt;TSource>(this IEnumerable&lt;TSource> source);
                /// methodinfo
                /// </summary>
                /// <param name="elementType">type of the elements</param>
                /// <returns></returns>
                public static MethodInfo GetCountMethodByType(Type elementType)
                {
                    // to get the method name, we use lambdas too
                    Expression<Action> methodNamer = () => GetCountMethod<object>();
                    var gmi = ((MethodCallExpression)methodNamer.Body).Method.GetGenericMethodDefinition();
                    var mi = gmi.MakeGenericMethod(new Type[] { elementType });
                    return mi.Invoke(null, new object[] { }) as MethodInfo;
                }
                #endregion Disctinct
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-08-13
              • 1970-01-01
              • 2013-02-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多