我正在尝试实现一种动态构建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 variables
或that 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
个孩子的方法。所以为了做到这一点,我必须做以下事情:
基本上我几乎就在那里,我所遇到的唯一问题如上所述 我的功能&lt;&gt;我们应该简单地称之为努力工作的方法。