从反射转移到表达式树

时间:2016-03-10 10:14:14

标签: c# reflection lambda expression-trees

我有一个反射代码,用于创建List<>的实例(运行时已知的类型参数),并调用Add方法向其中添加一些值。我的代码片段是这样的:

// here is my type parameter
var genericType = typeof(MyRunTimeType);
// here is a list of my values
MyRunTimeType[] values = MyRunTimeValuesOfTypeMyRunTimeType();

// creating instance of List<>
var listType = typeof(List<>);
var listGenericType = listType.MakeGenericType(genericType);
var listInstance = Activator.CreateInstance(listGenericType);

// getting Add method and call it
var addMethod = listGenericType.GetMethod("Add", genericType);
foreach (var value invalues)
    addMethod.Invoke(listInstance, new[] { value });

那么,您如何建议将此反射片段转换为表达式树?

更新

好吧,我写了这个片段,似乎已经关闭了工作:

public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType) {

    var listType = typeof(List<>);
    var listGenericType = listType.MakeGenericType(genericType);

    var values = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(genericType), "values");

    var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
    var instance = Expression.Parameter(listGenericType, "list");

    var assign = Expression.Assign(instance, Expression.New(ctor));

    var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });

    var addCall = Expression.Call(instance, addMethod, new Expression[] { values });

    var block = Expression.Block(
          new[] { instance },
          assign,
          addCall,
          Expression.Convert(instance, typeof(object))
        );

    return (Func<IEnumerable<object>, object>)Expression.Lambda(block, values).Compile();
}

但是,我收到了这个错误:

Unable to cast object of type 
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.String],System.Object]' 
to type 
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.Object],System.Object]'.

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

工作:

Func<IEnumerable<object>, object>

您的问题在于您尝试返回Func<IEnumerable<T>, object>,但您的功能实际上是IEnumerable<object>。解决方案是将参数设为Enumerable.Cast<T>,然后在传递给AddRange之前使用Expression.Parameter

我已将用于instance的{​​{1}}更改为Expression.Variable ...但只是为了更清楚地表明它是变量,而不是参数。由Expression.VariableExpression.Parameter生成的表达式树是相同的(因为这两个函数具有相同的代码)。它是使用它定义它是参数还是变量的上下文。我做了另一个小改动:Expression.Call不需要为参数进行显式数组初始化。

啊......请注意Block的最后一行可能是:

addCall,
instance

而不是

addCall,    
Expression.Convert(instance, typeof(object))

因为任何引用类型都可以隐式转换为object