c# - 从反射转向表达式树

标签 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]'.

请问有什么建议吗?

最佳答案

工作:

public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType)
{
    var listType = typeof(List<>);
    var listGenericType = listType.MakeGenericType(genericType);

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

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

    // I prefer using Expression.Variable to Expression.Parameter
    // for internal variables
    var instance = Expression.Variable(listGenericType, "list");

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

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

    // Enumerable.Cast<T>
    var castMethod = typeof(Enumerable).GetMethod("Cast", new[] { typeof(IEnumerable) }).MakeGenericMethod(genericType);

    // For the parameters there is a params Expression[], so no explicit array necessary
    var castCall = Expression.Call(castMethod, values);
    var addCall = Expression.Call(instance, addMethod, castCall);

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

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

您的问题在于您试图返回 Func<IEnumerable<object>, object>但你的功能实际上是 Func<IEnumerable<T>, object> 。解决办法就是将参数设为 IEnumerable<object>然后使用 Enumerable.Cast<T>在传递到 AddRange 之前

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

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

addCall,
instance

而不是

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

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

关于c# - 从反射转向表达式树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35913495/

相关文章:

node.js - λENOENT : no such file or directory

c# - ID 键从 Label 到 int 的转换问题

c# - 在页面之间传递 session 变量

actionscript-3 - AS3反射。如何判断一个方法是否被覆盖?

c# - 在运行时创建具有新匿名类型的 lambda 表达式

python-3.x - 我应该如何处理 AWS lambda 实现中的 joblib 多处理?

haskell - 如何为 lambda 中的运算符赋予不定性?

c# - 仅在下载后计算一次下载

c# - 通过 ado.net 从 C# 调用 SQL Server 存储过程超时

java - 是否可以创建一个在运行时反射(reflect)类的 List<class> ?