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

mcl*_*129 1 .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)
Run Code Online (Sandbox Code Playgroud)

不幸的是,实际上我不知道我拥有的集合是类型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)
Run Code Online (Sandbox Code Playgroud)

现在希望剩下的就是建立就是呼唤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)
Run Code Online (Sandbox Code Playgroud)

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

Dim alternateExp = Expression.Call(GetType(Queryable), "Select", Nothing, {targetProperty, item})
Run Code Online (Sandbox Code Playgroud)

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

Ada*_*lls 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);
Run Code Online (Sandbox Code Playgroud)

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