使用带有Queryable.Select的Expression.Call,其类型仅在运行时已知

时间:2012-07-03 14:00:04

标签: .net vb.net reflection iqueryable linq-expressions

我正在尝试从IEnumerable集合中选择一个列,该集合在运行时只有我知道的类型。我能想到使用它的唯一方法是使用LINQ表达式来构建对Queryable.Select的动态调用。但是,我在确定实现此目的的正确语法方面遇到了很多麻烦。

一个例子,说明如何在快乐 - 幸运的世界中这样做,在编译时知道我需要的一切,我的代码看起来像这样:

' Create an IEnumerable(Of String)
Dim strings = { "one", "two", "three" }

' Produce a collection with elements {3,3,5}
Dim stringLengths = strings.Select(Function(x) x.Length)

不幸的是,实际上我不知道我拥有的集合是String类型,或者我想要选择的属性是Length。我所拥有的是IEnumerable项集合,以及我想要选择的列的PropertyInfo,它为我提供了所需的所有类型信息。

就表达式而言,我已经能够创建一个LINQ表达式,我相信它将代表我通常会传递给选择的lambda(假设我尝试使用String和String.Length执行上述相同的操作) )

' pi is the PropertyInfo containing the Length property I am trying to select.
' pi.DeclaringType is String and pi.Name is Length
Dim targetItem = Expression.Parameter(pi.DeclaringType, "x")
Dim targetProperty = Expression.Property(targetItem, pi.Name)

' Produces the lambda<Function(x) x.Length>
Dim selectLambda = Expression.Lambda(targetProperty, targetItem)

现在希望剩下的就是建立对Queryable.Select的呼唤。对我来说,Expression.Call的语法至少可以说是令人困惑的。我的尝试如下(失败,没有任何错误或解释):

' Creates a Parameter Expression of type IQueryable(Of String)
Dim source = Expression.Parameter(GetType(IQueryable(Of )).MakeGenericType(pi.DeclaringType), "source")

' Ideally, this would create an expression for a call to Queryable.Select
Dim selectCall = Expression.Call(GetType(Queryable), "Select", {pi.DeclaringType, pi.PropertyType}, source)

我尝试以另一种方式执行此操作,而不使用Type []参数并使用我的item和property的表达式无效:

Dim alternateExp = Expression.Call(GetType(Queryable), "Select", Nothing, {targetProperty, item})

问题在于我只是在猜测这一点。但是,构建函数调用的整个想法,何时使用类型或表达式,使用哪种类型或表达式,或者甚至使用它们只是简单的混淆。任何帮助让我在那里的最后一部分和清除这个神秘的一部分将不胜感激。 (我对C#中的例子非常满意)

1 个答案:

答案 0 :(得分:5)

 var propertyType = typeof (string);
 var propertyName = "Length";
 IEnumerable list = new ArrayList { "one", "two", "three" };


  var item = Expression.Parameter(typeof(object), "x");
  var cast = Expression.Convert(item, propertyType);
  var propertyValue = Expression.PropertyOrField(cast, propertyName);
  var propertyValueAsObject = Expression.Convert(propertyValue, typeof (object));
  var selectLambda = Expression.Lambda<Func<object, object>>(propertyValueAsObject, item);

  list.Cast<object>().AsQueryable().Select(selectLambda);

这是一个使用表达式的答案,基本上我们将所有内容都作为Object处理(转换为我们的运行时类型,然后转换回Object以获得最终结果。