Использование Expression.Call с 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, которое, как мне кажется, будет представлять лямбду, которую я обычно передаю для выбора (предположим, я пытаюсь выполнить ту же операцию, что и выше, со строкой и строкой.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})

Проблема в том, что я в значительной степени только предполагаю на данный момент. Однако вся идея создания вызова функции, когда использовать типы или выражения, какие типы или выражения использовать или даже где их использовать, просто сбивает с толку. Буду очень признателен за любую помощь, которая поможет мне пройти последнюю часть пути и прояснить часть этой тайны. (я вполне доволен примерами на С#)


person mclark1129    schedule 03.07.2012    source источник


Ответы (1)


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

Это ответ с использованием выражений, в основном мы имеем дело со всем как с объектом (приведение к нашему типу среды выполнения, а затем обратное приведение к объекту для окончательного результата.

person Adam Mills    schedule 03.07.2012
comment
Спасибо, это сработало отлично! И даже не требовалось использовать сбивающий с толку Expression.Call. Надеюсь, я смогу расширить это для выполнения других операций, таких как группировка. - person mclark1129; 03.07.2012