2012-07-03 38 views
1

我试图从IEnumerable集合中选择一个只有我在运行时才知道的类型的列。我能想到的唯一方法是使用LINQ表达式来构建一个动态调用Queryable.Select。但是,我很难找出正确的语法来完成这个任务。使用Expression.Call 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执行上面的相同操作。长度)

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

我试图以另一种方式,而不使用类型做这个[]参数,并使用我的项目和属性来表达无效:

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

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

回答

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

这是使用表达式的答案,基本上我们处理每件事情都作为对象(铸造我们的运行时类型,然后铸造回对象的最终结果。

+0

谢谢,完美的工作!它甚至不需要使用令人困惑的Expression.Call。希望我可以扩展它来执行其他操作,例如分组。 – mclark1129

相关问题