【问题标题】:Moving from reflection to expression-tree从反射转向表达式树
【发布时间】:2016-03-10 10:14:13
【问题描述】:

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

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

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

更新:

好吧,我写了这个 sn-p,它似乎无法工作:

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

有什么建议吗?

【问题讨论】:

  • 为什么不直接在MyRunTimeValuesOfTypeMyRunTimeType() 上致电ToList()?这是一个数组,对吧?
  • 这不是一个问题,但你希望有人来做你的工作......你应该自己开始做,如果你发现你的代码中有一些阻塞问题,你可以再来这里询问具体的问题。
  • @MatíasFidemraizer 阻碍点是:我应该如何开始?
  • @Javad_Amiry 这不足以在 SO 中提出问题...开始阅读有关如何使用表达式树的 MSDN 文章,进行自己的测试,...
  • @MatíasFidemraizer 请查看更新

标签: c# reflection lambda expression-trees


【解决方案1】:

工作:

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&lt;IEnumerable&lt;object&gt;, object&gt;,但您的函数实际上是Func&lt;IEnumerable&lt;T&gt;, object&gt;。解决方法是将参数设为IEnumerable&lt;object&gt;,然后在传递给AddRange之前使用Enumerable.Cast&lt;T&gt;

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

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

addCall,
instance

而不是存在

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

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

【讨论】:

  • 它就像一个魅力:) 我忘了castMethod。谢谢;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-09
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
相关资源
最近更新 更多