【问题标题】:IQueryable OfType<T> where T is a runtime TypeIQueryable OfType<T> 其中 T 是运行时类型
【发布时间】:2011-04-09 20:16:24
【问题描述】:

我需要能够得到与以下类似的东西才能工作:

Type type = ??? // something decided at runtime with .GetType or typeof;
object[] entityList = context.Resources.OfType<type>().ToList();

这可能吗?如果有任何新功能允许,我可以使用 .NET 4。

【问题讨论】:

  • 必须是 IQueryable 吗?从您的示例来看, IEnumerable 似乎就足够了。
  • @leppie 我不明白你的意思..

标签: c# linq reflection iqueryable


【解决方案1】:

你可以通过反射调用它:

MethodInfo method = typeof(Queryable).GetMethod("OfType");
MethodInfo generic = method.MakeGenericMethod(new Type[]{ type });
// Use .NET 4 covariance
var result = (IEnumerable<object>) generic.Invoke
      (null, new object[] { context.Resources });
object[] array = result.ToArray();

另一种方法是编写自己的 OfTypeAndToArray 通用方法来完成这两个方面,但上述方法应该可以工作。

【讨论】:

  • Skeet 这行得通,真是个传奇——我将使用 SQL 分析器来查看它是否在检索集合后进行过滤或作为查询的一部分。除非你知道答案那也是!
  • @Shahin:它应该做正确的事——毕竟它调用的是Queryable.Where而不是Enumerable.Where
  • @Skeet 你知道为什么它可能会从它收到的第一个结果集中缓存吗?
  • 因为我花了大约 20 分钟试图弄清楚一些事情,所以我会在这里评论它,以便其他人不要。它将 context.Resources 对象传递到 'parameters' 参数中的一个数组中,因为 OfType 是一个扩展方法,并将它正在扩展的类作为参数(当我得到它时它让我大吃一惊)。
  • 我看到这会返回一个带有.ToArray() 的数组,所以我猜这会在调用后执行查询。将 IQueryable() 保留为结果会太不同,因此您可以在最终运行查询之前继续链接其他方法?
【解决方案2】:

看起来你需要在这里使用反射......

public static IEnumerable<object> DyamicOfType<T>(
        this IQueryable<T> input, Type type)
{
    var ofType = typeof(Queryable).GetMethod("OfType",
                     BindingFlags.Static | BindingFlags.Public);
    var ofTypeT = ofType.MakeGenericMethod(type);
    return (IEnumerable<object>) ofTypeT.Invoke(null, new object[] { input });
}

Type type = // ...;
var entityList = context.Resources.DynamicOfType(type).ToList();

【讨论】:

    【解决方案3】:

    处理多种类型的解决方案是

            public static IQueryable<TEntity> OfTypes<TEntity>(this DbSet<TEntity> query, IEnumerable<Type> types )  where TEntity : class
                {
                        if( types.Count() == 0 ) return query;
    
                        var lambda = GetOfOnlyTypesPredicate( typeof(TEntity), types.ToArray() );
                        return query.OfType<TEntity>().Where( lambda as Expression<Func<TEntity,bool>>);
    
                }
    
    
                public static LambdaExpression GetOfOnlyTypesPredicate( Type baseType, Type[] allowed )
                {
                        ParameterExpression param = Expression.Parameter( baseType, "typeonlyParam" );
                        Expression merged = Expression.TypeIs( param, allowed[0] );
                        for( int i = 1; i < allowed.Length; i++ )
                                merged = Expression.OrElse( merged, Expression.TypeIs( param, allowed[i] ));
                        return Expression.Lambda( merged, param );
    

    【讨论】:

      【解决方案4】:

      怎么样...

          public static IList OfTypeToList(this IEnumerable source, Type type)
          {
              if (type == null)
                  throw new ArgumentNullException(nameof(type));
              return
                  (IList) Activator.CreateInstance(
                      typeof(List<>)
                         .MakeGenericType(type),
                      typeof(System.Linq.Enumerable)
                         .GetMethod(nameof(System.Linq.Enumerable.OfType),
                                    BindingFlags.Static | BindingFlags.Public)
                         .MakeGenericMethod(type)
                         .Invoke(null, new object[] { source }));
          }
      

      【讨论】:

        【解决方案5】:

        基于@Jon Skeetanswer,这里有一个LINQ扩展方法:

        public static class QueryableExtensions
        {
            public static IQueryable<TSource> OfType<TSource>(this IQueryable<TSource> queryable,
                Type runtimeType)
            {
                var method = typeof(Queryable).GetMethod(nameof(Queryable.OfType));
                var generic = method.MakeGenericMethod(new[] { runtimeType });
                return (IQueryable<TSource>)generic.Invoke(null, new[] { queryable });
            }
        }
        

        【讨论】:

          【解决方案6】:

          纯粹根据您的问题使用“泛型”,不,这是不可能的。

          泛型是一种编译时特性,而不是运行时发现。对于运行时,您需要使用反射或动态。

          【讨论】:

          • 嗯,我不喜欢“泛型是编译时特性”的描述。就是这样,而且显然也是一个运行时特性(与 Java 不同)。
          • 你可能不喜欢它,但重要的是泛型是一个编译时特性。泛型类型是用T的知识编译出来的,如果能说明,请赐教。
          • 如果它不是运行时功能,MakeGenericType 怎么能工作?
          【解决方案7】:
          Resource[] entityList = context.Resources
                                        .Where(t => t.GetType() == typeof(HumanResource))
                                        .ToArray();
          

          【讨论】:

          • 在运行 where 之前不会返回 Resources 的全部内容吗?在此上下文中使用 OfType 在 SQL 中运行它(我正在使用 Zentity)
          • 除了Shahin所说的,这对于多态类型也是错误的。
          【解决方案8】:

          这对我有用...

          Type type = queryable.GetType().GenericTypeArguments[0];
          

          【讨论】:

            猜你喜欢
            • 2016-01-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多