1

我正在尝试从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)

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

就表达式而言,我已经能够创建一个 LINQ 表达式,我相信它可以代表我通常会传递给 select 的 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# 中的示例非常满意)

4

1 回答 1

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);

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

于 2012-07-03T14:59:03.723 回答