Expression.Block不同的签名将参数值更改为null

时间:2017-09-25 14:07:22

标签: c# generics reflection .net-core

我正在尝试实现一种动态构建Func<,>的方法,具体取决于提供的类型。 Func本身应该简单地调用静态方法并返回其第一个参数。我有几个虚拟课程:

public class Foo { }
public class Foo1 : Foo { }
public class Foo2 : Foo { }
public class Foo3 : Foo { }

方法的用法如下:

var genericTypes = new List<Type>() { typeof(Foo1), typeof(Foo2), typeof(Foo3) };
var func = BuildFunc(genericTypes);
var res = func.DynamicInvoke(new Foo1(), new Foo2(), new Foo3());

当我进行推荐时,它工作正常。然而,当我试图正确地实现它时,我遇到了一个我无法解释的奇怪行为。

以下是我实施该方法的方法:

private static Delegate BuildFunc(List<Type> genericTypes)
{
    List<ParameterExpression> parameters = new List<ParameterExpression>();
    for (int i = 0; i < genericTypes.Count; i++)
    {
        var param = Expression.Parameter(genericTypes[i], "arg" + i);
        parameters.Add(param);
    }

    var methodCallInfo = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Single(a => a.Name == "PopulateChildren");
    var parameterArray = Expression.NewArrayInit(typeof(Foo), parameters);

    var callExpr = Expression.Call(methodCallInfo,
        parameterArray);

    //return arg0
    LabelTarget returnTarget = Expression.Label(type: parameters[0].Type);
    GotoExpression returnExpression = Expression.Return(returnTarget,
        parameters[0], parameters[0].Type);
    var returnLabel = Expression.Label(returnTarget, parameters[0]);


    //assembling & compiling
    var assembled = Expression.Block(
            parameters,
            callExpr,
            returnExpression,
            returnLabel);

    var compiled = Expression.Lambda(assembled, parameters).Compile();
    return compiled;
}

public static void PopulateChildren(Foo[] objects)
{

}

正如您所看到的,上面的代码定义了参数,使用参数初始化数组,调用方法并返回其第一个参数。

现在,如果您运行此代码并逐步执行PopulateChildren方法 - 您将获得具有3个空值的objects集合。

如果你改变了

    var assembled = Expression.Block(
        parameters,
        callExpr,
        returnExpression,
        returnLabel);

    var assembled = Expression.Block(
        parameters[0],
        parameters[1],
        parameters[2],
        callExpr,
        returnExpression,
        returnLabel);

你会得到价值! 现在我发现Expression.Block的不同签名在其摘要中包含has no variablesthat contains the given variables个字词。

我发现这个方法的签名在这里超载了https://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.block(v=vs.100).aspx,我心里想:&#34;好吧,我只是愚蠢而且意外地使用了错误的签名。我会用一个正确的!&#34;

所以我选择了这个: Block(IEnumerable<ParameterExpression>, IEnumerable<Expression>),其中包含说明Creates a BlockExpression that contains the given variables and expressions.

所以我将上述代码更改为

        var expresssion = new List<Expression>()
        {
            callExpr,
            returnExpression,
            returnLabel
        };
        var assembled = Expression.Block(parameters, expresssion);

它仍然给我空值!

有人可以向我解释发生了什么以及我遗失了什么? 最重要的是,我如何以接受参数集合的方式调用Expression.Block并且不将它们变为空?

澄清

这个想法的诞生是因为尝试围绕Dapper构建ORM。 Dapper允许多重映射https://github.com/StackExchange/Dapper#multi-mapping

使用的方式是

    var sql = 
    @"select * from #Posts p 
    left join #Users u on u.Id = p.OwnerId 
    Order by p.Id";

    var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});

最重要的是,您可以在数据库的一次旅行中获得对象的层次结构。我不喜欢的部分是你必须将类型指定为泛型参数。我发现这是必要的,但我想更进一步,提供一种简单的方式来呼叫GetWithChildren<User>(id)并接收User填充Post个孩子的方法。所以为了做到这一点,我必须做以下事情:

  1. 构建依赖子类型(表)关系树
  2. 构建一个将选择它们的SQL查询
  3. 构建一个Func&lt;&gt;填充儿童/父母关系
  4. 拨打电话以更正查询&lt;&gt;方法并传递一个Func
  5. 基本上我几乎就在那里,我所遇到的唯一问题如上所述 我的功能&lt;&gt;我们应该简单地称之为努力工作的方法。

0 个答案:

没有答案