使用 Expression.Call with Queryable.Select 使用仅在运行时已知的类型



我正在尝试从 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[] 参数并使用我的项目和属性的表达式,但无济于事:

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

问题是,在这一点上,我几乎只是猜测。 但是,构建函数调用的整个想法,何时使用类型或表达式,使用哪些类型或表达式,甚至在哪里使用它们都是令人困惑的。 任何帮助我到达那里的最后一部分并清除一些这个谜团将不胜感激。(我对 C# 中的示例非常满意)

 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 以获得最终结果。

最新更新